stackit-core 0.2.0__tar.gz → 0.3.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.
@@ -0,0 +1,267 @@
1
+ ### VisualStudioCode template
2
+ .vscode/*
3
+ !.vscode/settings.json
4
+ !.vscode/tasks.json
5
+ !.vscode/launch.json
6
+ !.vscode/extensions.json
7
+ !.vscode/*.code-snippets
8
+
9
+ # Local History for Visual Studio Code
10
+ .history/
11
+
12
+ # Built Visual Studio Code Extensions
13
+ *.vsix
14
+
15
+ ### JetBrains template
16
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
17
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
18
+
19
+ # User-specific stuff
20
+ .idea/**/workspace.xml
21
+ .idea/**/tasks.xml
22
+ .idea/**/usage.statistics.xml
23
+ .idea/**/dictionaries
24
+ .idea/**/shelf
25
+
26
+ # AWS User-specific
27
+ .idea/**/aws.xml
28
+
29
+ # Generated files
30
+ .idea/**/contentModel.xml
31
+
32
+ # Sensitive or high-churn files
33
+ .idea/**/dataSources/
34
+ .idea/**/dataSources.ids
35
+ .idea/**/dataSources.local.xml
36
+ .idea/**/sqlDataSources.xml
37
+ .idea/**/dynamic.xml
38
+ .idea/**/uiDesigner.xml
39
+ .idea/**/dbnavigator.xml
40
+
41
+ # Gradle
42
+ .idea/**/gradle.xml
43
+ .idea/**/libraries
44
+
45
+ # Gradle and Maven with auto-import
46
+ # When using Gradle or Maven with auto-import, you should exclude module files,
47
+ # since they will be recreated, and may cause churn. Uncomment if using
48
+ # auto-import.
49
+ # .idea/artifacts
50
+ # .idea/compiler.xml
51
+ # .idea/jarRepositories.xml
52
+ # .idea/modules.xml
53
+ # .idea/*.iml
54
+ # .idea/modules
55
+ # *.iml
56
+ # *.ipr
57
+
58
+ # CMake
59
+ cmake-build-*/
60
+
61
+ # Mongo Explorer plugin
62
+ .idea/**/mongoSettings.xml
63
+
64
+ # File-based project format
65
+ *.iws
66
+
67
+ # IntelliJ
68
+ out/
69
+
70
+ # mpeltonen/sbt-idea plugin
71
+ .idea_modules/
72
+
73
+ # JIRA plugin
74
+ atlassian-ide-plugin.xml
75
+
76
+ # Cursive Clojure plugin
77
+ .idea/replstate.xml
78
+
79
+ # SonarLint plugin
80
+ .idea/sonarlint/
81
+
82
+ # Crashlytics plugin (for Android Studio and IntelliJ)
83
+ com_crashlytics_export_strings.xml
84
+ crashlytics.properties
85
+ crashlytics-build.properties
86
+ fabric.properties
87
+
88
+ # Editor-based Rest Client
89
+ .idea/httpRequests
90
+
91
+ # Android studio 3.1+ serialized cache file
92
+ .idea/caches/build_file_checksums.ser
93
+
94
+ ### VirtualEnv template
95
+ # Virtualenv
96
+ # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
97
+ .Python
98
+ [Bb]in
99
+ [Ii]nclude
100
+ [Ll]ib
101
+ [Ll]ib64
102
+ [Ll]ocal
103
+ [Ss]cripts
104
+ pyvenv.cfg
105
+ .venv
106
+ pip-selfcheck.json
107
+
108
+ ### Python template
109
+ # Byte-compiled / optimized / DLL files
110
+ __pycache__/
111
+ *.py[cod]
112
+ *$py.class
113
+
114
+ # C extensions
115
+ *.so
116
+
117
+ # Distribution / packaging
118
+ build/
119
+ develop-eggs/
120
+ dist/
121
+ downloads/
122
+ eggs/
123
+ .eggs/
124
+ lib/
125
+ lib64/
126
+ parts/
127
+ sdist/
128
+ var/
129
+ wheels/
130
+ share/python-wheels/
131
+ *.egg-info/
132
+ .installed.cfg
133
+ *.egg
134
+ MANIFEST
135
+
136
+ # PyInstaller
137
+ # Usually these files are written by a python script from a template
138
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
139
+ *.manifest
140
+ *.spec
141
+
142
+ # Installer logs
143
+ pip-log.txt
144
+ pip-delete-this-directory.txt
145
+
146
+ # Unit test / coverage reports
147
+ htmlcov/
148
+ .tox/
149
+ .nox/
150
+ .coverage
151
+ .coverage.*
152
+ .cache
153
+ nosetests.xml
154
+ coverage.xml
155
+ *.cover
156
+ *.py,cover
157
+ .hypothesis/
158
+ .pytest_cache/
159
+ cover/
160
+
161
+ # Translations
162
+ *.mo
163
+ *.pot
164
+
165
+ # Django stuff:
166
+ *.log
167
+ local_settings.py
168
+ db.sqlite3
169
+ db.sqlite3-journal
170
+
171
+ # Flask stuff:
172
+ instance/
173
+ .webassets-cache
174
+
175
+ # Scrapy stuff:
176
+ .scrapy
177
+
178
+ # Sphinx documentation
179
+ docs/_build/
180
+
181
+ # PyBuilder
182
+ .pybuilder/
183
+ target/
184
+
185
+ # Jupyter Notebook
186
+ .ipynb_checkpoints
187
+
188
+ # IPython
189
+ profile_default/
190
+ ipython_config.py
191
+
192
+ # pyenv
193
+ # For a library or package, you might want to ignore these files since the code is
194
+ # intended to run in multiple environments; otherwise, check them in:
195
+ # .python-version
196
+
197
+ # pipenv
198
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
199
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
200
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
201
+ # install all needed dependencies.
202
+ #Pipfile.lock
203
+
204
+ # poetry
205
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
206
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
207
+ # commonly ignored for libraries.
208
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
209
+ #poetry.lock
210
+
211
+ # pdm
212
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
213
+ #pdm.lock
214
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
215
+ # in version control.
216
+ # https://pdm.fming.dev/#use-with-ide
217
+ .pdm.toml
218
+
219
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
220
+ __pypackages__/
221
+
222
+ # Celery stuff
223
+ celerybeat-schedule
224
+ celerybeat.pid
225
+
226
+ # SageMath parsed files
227
+ *.sage.py
228
+
229
+ # Environments
230
+ .env
231
+ env/
232
+ venv/
233
+ ENV/
234
+ env.bak/
235
+ venv.bak/
236
+
237
+ # Spyder project settings
238
+ .spyderproject
239
+ .spyproject
240
+
241
+ # Rope project settings
242
+ .ropeproject
243
+
244
+ # mkdocs documentation
245
+ /site
246
+
247
+ # mypy
248
+ .mypy_cache/
249
+ .dmypy.json
250
+ dmypy.json
251
+
252
+ # Pyre type checker
253
+ .pyre/
254
+
255
+ # pytype static type analyzer
256
+ .pytype/
257
+
258
+ # Cython debug symbols
259
+ cython_debug/
260
+
261
+ # PyCharm
262
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
263
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
264
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
265
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
266
+ .idea/
267
+
@@ -1,24 +1,26 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: stackit-core
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Core functionality for the STACKIT SDK for Python
5
- Author: STACKIT Developer Tools
6
- Author-email: developer-tools@stackit.cloud
7
- Requires-Python: >=3.8,<4.0
5
+ Project-URL: Homepage, https://github.com/stackitcloud/stackit-sdk-python
6
+ Project-URL: Issues, https://github.com/stackitcloud/stackit-sdk-python/issues
7
+ Author-email: STACKIT Developer Tools <developer-tools@stackit.cloud>
8
+ License-File: LICENSE.md
9
+ License-File: NOTICE.txt
8
10
  Classifier: License :: OSI Approved :: Apache Software License
9
11
  Classifier: Operating System :: OS Independent
10
12
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
- Requires-Dist: cryptography (>=43.0.1)
18
- Requires-Dist: pydantic (>=2.9.2)
19
- Requires-Dist: pyjwt (>=2.9.0)
20
- Requires-Dist: requests (>=2.32.3)
21
- Requires-Dist: urllib3 (>=2.2.3)
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: <4.0,>=3.10
19
+ Requires-Dist: cryptography>=46.0.5
20
+ Requires-Dist: pydantic>=2.9.2
21
+ Requires-Dist: pyjwt>=2.13.0
22
+ Requires-Dist: requests>=2.33.0
23
+ Requires-Dist: urllib3>=2.7.0
22
24
  Description-Content-Type: text/markdown
23
25
 
24
26
  > ⓘ INFO: The STACKIT Python SDK is in beta and in active development.
@@ -34,4 +36,3 @@ to use the [STACKIT Python SDK](https://github.com/stackitcloud/stackit-sdk-pyth
34
36
  ## Usage
35
37
 
36
38
  The core package does nothing on its own and must be used in conjunction with the services of the [STACKIT Python SDK](https://github.com/stackitcloud/stackit-sdk-python).
37
-
@@ -0,0 +1,121 @@
1
+ [project]
2
+ name = "stackit-core"
3
+ version = "v0.3.0"
4
+ description = "Core functionality for the STACKIT SDK for Python"
5
+ authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }]
6
+ requires-python = ">=3.10,<4.0"
7
+ readme = "README.md"
8
+ classifiers = [
9
+ "Programming Language :: Python :: 3",
10
+ "License :: OSI Approved :: Apache Software License",
11
+ "Operating System :: OS Independent",
12
+ "Programming Language :: Python :: 3.10",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
17
+ ]
18
+ dependencies = [
19
+ "pyjwt>=2.13.0",
20
+ "pydantic>=2.9.2",
21
+ "urllib3>=2.7.0",
22
+ "cryptography>=46.0.5",
23
+ "requests>=2.33.0",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/stackitcloud/stackit-sdk-python"
28
+ Issues = "https://github.com/stackitcloud/stackit-sdk-python/issues"
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "black>=26.3.1",
33
+ "pytest>=9.1.1",
34
+ "flake8>=5.0.3 ; python_full_version < '3.12'",
35
+ "flake8>=6.0.1 ; python_full_version >= '3.12'",
36
+ "flake8-black>=0.3.6",
37
+ "flake8-pyproject>=1.2.3",
38
+ "flake8-quotes>=3.4.0",
39
+ "flake8-bandit>=4.1.1",
40
+ "flake8-bugbear>=23.1.14",
41
+ "flake8-eradicate>=1.5.0",
42
+ "flake8-eol>=0.0.8",
43
+ "autoimport>=1.6.1",
44
+ "isort>=5.13.2",
45
+ ]
46
+
47
+ [tool.uv]
48
+ default-groups = "all"
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = ["src/stackit"]
52
+
53
+ [tool.hatch.build.targets.wheel]
54
+ include = ["src/stackit"]
55
+
56
+ [tool.hatch.build.targets.wheel.sources]
57
+ "src/stackit" = "stackit"
58
+
59
+ [build-system]
60
+ requires = ["hatchling"]
61
+ build-backend = "hatchling.build"
62
+
63
+ [tool.pytest.ini_options]
64
+ pythonpath = ["src"]
65
+ testpaths = ["tests"]
66
+
67
+ [tool.black]
68
+ line-length = 120
69
+ exclude = """
70
+ /(
71
+ .eggs
72
+ | .git
73
+ | .hg
74
+ | .mypy_cache
75
+ | .nox
76
+ | .pants.d
77
+ | .tox
78
+ | .venv
79
+ | _build
80
+ | buck-out
81
+ | build
82
+ | dist
83
+ | node_modules
84
+ | venv
85
+ )/
86
+ """
87
+
88
+ [tool.isort]
89
+ profile = 'black'
90
+
91
+ [tool.flake8]
92
+ exclude = [
93
+ ".eggs",
94
+ ".git",
95
+ ".hg",
96
+ ".mypy_cache",
97
+ ".tox",
98
+ ".venv",
99
+ ".devcontainer",
100
+ "venv",
101
+ "_build",
102
+ "buck-out",
103
+ "build",
104
+ "dist",
105
+ ]
106
+ statistics = true
107
+ show-source = false
108
+ max-line-length = 120
109
+ # E203,W503 and E704 are incompatible with the formatter black
110
+ # fixing B042 in upstream exceptions.mustache would be a breaking change
111
+ ignore = ["E203", "W503", "E704", "B042"]
112
+ inline-quotes = '"'
113
+ docstring-quotes = '"""'
114
+ multiline-quotes = '"""'
115
+ ban-relative-imports = true
116
+ per-file-ignores = """
117
+ # S101: asserts are fine in tests, tests shouldn't be build optimized
118
+ # S105: hardcoded passwords in tests are fine
119
+ # S106: hardcoded passwords in tests are fine
120
+ ./tests/*: S101,S105,S106,
121
+ """
@@ -42,6 +42,7 @@ class KeyAuth(AuthBase):
42
42
  DEFAULT_TOKEN_ENDPOINT = "https://service-account.api.stackit.cloud/token" # noqa S105 false positive
43
43
  TOKEN_EXPIRY_CHECK_INTERVAL = timedelta(seconds=60)
44
44
  EXPIRATION_LEEWAY = timedelta(minutes=5)
45
+ MAX_REFRESH_RETRIES = 3
45
46
 
46
47
  timeout: Optional[int] = 30
47
48
  initial_token: Optional[str]
@@ -73,6 +74,8 @@ class KeyAuth(AuthBase):
73
74
 
74
75
  def __call__(self, r: Request) -> Request:
75
76
  with self.lock:
77
+ if self.refresh_future is not None and self.refresh_future.done():
78
+ self.refresh_future.result()
76
79
  if self.__is_token_expired(self.access_token):
77
80
  if self.refresh_future is None or self.refresh_future.done():
78
81
  self.refresh_future = self.executor.submit(self.__refresh_token)
@@ -108,13 +111,15 @@ class KeyAuth(AuthBase):
108
111
  self.access_token = response_json["access_token"]
109
112
  self.refresh_token = response_json["refresh_token"]
110
113
  except requests.RequestException as e:
111
- print(f"Initial token fetch failed: {e}")
114
+ raise requests.RequestException("Initial token fetch failed") from e
112
115
 
113
116
  def __start_token_refresh_task(self):
114
117
  def token_refresh_task():
115
118
  while True:
116
119
  time.sleep(self.TOKEN_EXPIRY_CHECK_INTERVAL.total_seconds())
117
120
  with self.lock:
121
+ if self.refresh_future is not None and self.refresh_future.done():
122
+ self.refresh_future.result()
118
123
  if self.__is_token_expired(self.access_token) and (
119
124
  self.refresh_future is None or self.refresh_future.done()
120
125
  ):
@@ -135,16 +140,19 @@ class KeyAuth(AuthBase):
135
140
  "refresh_token": self.refresh_token,
136
141
  }
137
142
 
138
- try:
139
- response = requests.post(self.token_endpoint, data=body, timeout=self.timeout)
140
- response.raise_for_status()
141
- response_data = response.json()
142
- new_token = response_data.get("access_token")
143
- # with self.lock:
144
- self.access_token = new_token
145
- print("Token successfully refreshed!")
146
- except requests.RequestException as e:
147
- print(f"Token refresh failed: {e}")
143
+ last_exception = None
144
+ for _ in range(self.MAX_REFRESH_RETRIES):
145
+ try:
146
+ response = requests.post(self.token_endpoint, data=body, timeout=self.timeout)
147
+ response.raise_for_status()
148
+ response_data = response.json()
149
+ new_token = response_data.get("access_token")
150
+ self.access_token = new_token
151
+ return
152
+ except requests.RequestException as e:
153
+ last_exception = e
154
+
155
+ raise requests.RequestException("Token refresh failed after retries") from last_exception
148
156
 
149
157
  def __is_token_expired(self, token: str) -> bool:
150
158
  try:
@@ -1,109 +0,0 @@
1
- [project]
2
- name = "stackit-core"
3
-
4
- [tool.poetry]
5
- name = "stackit-core"
6
- version = "v0.2.0"
7
- authors = ["STACKIT Developer Tools <developer-tools@stackit.cloud>"]
8
- description = "Core functionality for the STACKIT SDK for Python"
9
- readme = "README.md"
10
- classifiers = [
11
- "Programming Language :: Python :: 3",
12
- "License :: OSI Approved :: Apache Software License",
13
- "Operating System :: OS Independent",
14
- ]
15
- packages = [{ include = "stackit", from = "src" }]
16
-
17
-
18
- [tool.poetry.dependencies]
19
- python = ">=3.8,<4.0"
20
- pyjwt = ">=2.9.0"
21
- pydantic = ">=2.9.2"
22
- urllib3 = ">=2.2.3"
23
- cryptography = ">=43.0.1"
24
- requests = ">=2.32.3"
25
-
26
-
27
- [tool.poetry.group.dev.dependencies]
28
- black = ">=24.8.0"
29
- pytest = ">=8.3.3"
30
- flake8 = [
31
- { version = ">=5.0.3", python = "<3.12" },
32
- { version = ">=6.0.1", python = ">=3.12" },
33
- ]
34
- flake8-black = ">=0.3.6"
35
- flake8-pyproject = ">=1.2.3"
36
- flake8-quotes = ">=3.4.0"
37
- flake8-bandit = ">=4.1.1"
38
- flake8-bugbear = ">=23.1.14"
39
- flake8-eradicate = ">=1.5.0"
40
- flake8-eol = ">=0.0.8"
41
- autoimport = ">=1.6.1"
42
- isort = ">=5.13.2"
43
-
44
- [project.urls]
45
- Homepage = "https://github.com/stackitcloud/stackit-sdk-python"
46
- Issues = "https://github.com/stackitcloud/stackit-sdk-python/issues"
47
-
48
- [build-system]
49
- requires = ["setuptools", "poetry-core"]
50
- build-backend = "poetry.core.masonry.api"
51
-
52
- [tool.pytest.ini_options]
53
- pythonpath = ["src"]
54
- testpaths = ["tests"]
55
-
56
- [tool.black]
57
- line-length = 120
58
- exclude = """
59
- /(
60
- .eggs
61
- | .git
62
- | .hg
63
- | .mypy_cache
64
- | .nox
65
- | .pants.d
66
- | .tox
67
- | .venv
68
- | _build
69
- | buck-out
70
- | build
71
- | dist
72
- | node_modules
73
- | venv
74
- )/
75
- """
76
-
77
- [tool.isort]
78
- profile = 'black'
79
-
80
- [tool.flake8]
81
- exclude = [
82
- ".eggs",
83
- ".git",
84
- ".hg",
85
- ".mypy_cache",
86
- ".tox",
87
- ".venv",
88
- ".devcontainer",
89
- "venv",
90
- "_build",
91
- "buck-out",
92
- "build",
93
- "dist",
94
- ]
95
- statistics = true
96
- show-source = false
97
- max-line-length = 120
98
- # E203,W503 and E704 are incompatible with the formatter black
99
- ignore = ["E203", "W503", "E704"]
100
- inline-quotes = '"'
101
- docstring-quotes = '"""'
102
- multiline-quotes = '"""'
103
- ban-relative-imports = true
104
- per-file-ignores = """
105
- # S101: asserts are fine in tests, tests shouldn't be build optimized
106
- # S105: hardcoded passwords in tests are fine
107
- # S106: hardcoded passwords in tests are fine
108
- ./tests/*: S101,S105,S106,
109
- """
File without changes
File without changes
File without changes