idegym-common-utils 0.9.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,294 @@
1
+ ### Python template
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+
7
+ # C extensions
8
+ *.so
9
+
10
+ # Distribution / packaging
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Installer logs
35
+ pip-log.txt
36
+ pip-delete-this-directory.txt
37
+
38
+ # Unit test / coverage reports
39
+ htmlcov/
40
+ .tox/
41
+ .nox/
42
+ .coverage
43
+ .coverage.*
44
+ .cache
45
+ nosetests.xml
46
+ coverage.xml
47
+ *.cover
48
+ *.py,cover
49
+ .hypothesis/
50
+ .pytest_cache/
51
+ cover/
52
+
53
+ # Translations
54
+ *.mo
55
+ *.pot
56
+
57
+ # Django stuff:
58
+ *.log
59
+ local_settings.py
60
+ db.sqlite3
61
+ db.sqlite3-journal
62
+
63
+ # Flask stuff:
64
+ instance/
65
+ .webassets-cache
66
+
67
+ # Scrapy stuff:
68
+ .scrapy
69
+
70
+ # Sphinx documentation
71
+ docs/_build/
72
+
73
+ # PyBuilder
74
+ .pybuilder/
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pdm
85
+ .pdm.toml
86
+ .pdm-python
87
+ .pdm-build/
88
+
89
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
90
+ __pypackages__/
91
+
92
+ # Celery stuff
93
+ celerybeat-schedule
94
+ celerybeat.pid
95
+
96
+ # SageMath parsed files
97
+ *.sage.py
98
+
99
+ # Environments
100
+ .env
101
+ .venv
102
+ env/
103
+ venv/
104
+ ENV/
105
+ env.bak/
106
+ venv.bak/
107
+
108
+ # Spyder project settings
109
+ .spyderproject
110
+ .spyproject
111
+
112
+ # Rope project settings
113
+ .ropeproject
114
+
115
+ # mkdocs documentation
116
+ /site
117
+
118
+ # mypy
119
+ .mypy_cache/
120
+ .dmypy.json
121
+ dmypy.json
122
+
123
+ # Pyre type checker
124
+ .pyre/
125
+
126
+ # pytype static type analyzer
127
+ .pytype/
128
+
129
+ # Cython debug symbols
130
+ cython_debug/
131
+
132
+ ### PyCharm template
133
+ # User-specific stuff
134
+ .idea/**/workspace.xml
135
+ .idea/**/tasks.xml
136
+ .idea/**/usage.statistics.xml
137
+ .idea/**/dictionaries
138
+ .idea/**/shelf
139
+
140
+ # AWS User-specific
141
+ .idea/**/aws.xml
142
+
143
+ # Generated files
144
+ .idea/**/contentModel.xml
145
+
146
+ # Sensitive or high-churn files
147
+ .idea/**/dataSources/
148
+ .idea/**/dataSources.ids
149
+ .idea/**/dataSources.local.xml
150
+ .idea/**/dataSources.xml
151
+ .idea/**/sqlDataSources.xml
152
+ .idea/**/dynamic.xml
153
+ .idea/**/uiDesigner.xml
154
+ .idea/**/dbnavigator.xml
155
+
156
+ # Gradle
157
+ .idea/**/gradle.xml
158
+ .idea/**/libraries
159
+
160
+ # Module files
161
+ *.iml
162
+
163
+ # Project structure
164
+ .idea/**/misc.xml
165
+ .idea/**/modules.xml
166
+ .idea/pySourceRootDetection.xml
167
+ .idea/**/inspectionProfiles/
168
+ .idea/**/runConfigurations/
169
+
170
+ # CMake
171
+ cmake-build-*/
172
+
173
+ # Mongo Explorer plugin
174
+ .idea/**/mongoSettings.xml
175
+
176
+ # File-based project format
177
+ *.iws
178
+
179
+ # IntelliJ
180
+ out/
181
+
182
+ # mpeltonen/sbt-idea plugin
183
+ .idea_modules/
184
+
185
+ # JIRA plugin
186
+ atlassian-ide-plugin.xml
187
+
188
+ # Cursive Clojure plugin
189
+ .idea/replstate.xml
190
+
191
+ # SonarLint plugin
192
+ .idea/sonarlint/
193
+
194
+ # Crashlytics plugin (for Android Studio and IntelliJ)
195
+ com_crashlytics_export_strings.xml
196
+ crashlytics.properties
197
+ crashlytics-build.properties
198
+ fabric.properties
199
+
200
+ # Editor-based Rest Client
201
+ .idea/httpRequests
202
+
203
+ # Android studio 3.1+ serialized cache file
204
+ .idea/caches/build_file_checksums.ser
205
+
206
+ ### Linux template
207
+ # General
208
+ *~
209
+
210
+ # temporary files which can be created if a process still has a handle open of a deleted file
211
+ .fuse_hidden*
212
+
213
+ # KDE directory preferences
214
+ .directory
215
+
216
+ # Linux trash folder which might appear on any partition or disk
217
+ .Trash-*
218
+
219
+ # .nfs files are created when an open file is removed but is still being accessed
220
+ .nfs*
221
+
222
+ ### macOS template
223
+ # General
224
+ .DS_Store
225
+ .AppleDouble
226
+ .LSOverride
227
+
228
+ # Icon must end with two \r
229
+ Icon
230
+
231
+ # Thumbnails
232
+ ._*
233
+
234
+ # Files that might appear in the root of a volume
235
+ .DocumentRevisions-V100
236
+ .fseventsd
237
+ .Spotlight-V100
238
+ .TemporaryItems
239
+ .Trashes
240
+ .VolumeIcon.icns
241
+ .com.apple.timemachine.donotpresent
242
+
243
+ # Directories potentially created on remote AFP share
244
+ .AppleDB
245
+ .AppleDesktop
246
+ Network Trash Folder
247
+ Temporary Items
248
+ .apdisk
249
+
250
+ ### Archives template
251
+ # It's better to unpack these files and commit the raw source because
252
+ # git has its own built in compression methods.
253
+ *.7z
254
+ *.jar
255
+ *.rar
256
+ *.zip
257
+ !plugins/pycharm/project-opener/project-opener.zip
258
+ !plugins/idea/project-opener/project-opener.zip
259
+ *.gz
260
+ *.gzip
261
+ *.tgz
262
+ *.bzip
263
+ *.bzip2
264
+ *.bz2
265
+ *.xz
266
+ *.lzma
267
+ *.cab
268
+ *.xar
269
+ *.zst
270
+ *.tzst
271
+
272
+ # Packing-only formats
273
+ *.iso
274
+ *.tar
275
+
276
+ # Package management formats
277
+ *.dmg
278
+ *.xpi
279
+ *.gem
280
+ *.deb
281
+ *.rpm
282
+ *.msi
283
+ *.msm
284
+ *.msp
285
+ *.txz
286
+
287
+ ### Dev Container template
288
+ .*.temp.dockerfile
289
+
290
+ ### Project template
291
+ .project/
292
+ /test-results.xml
293
+ examples/*.json
294
+ examples/**/*.patch
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: idegym-common-utils
3
+ Version: 0.9.0
4
+ Summary: IdeGYM Common Utils
5
+ Author-email: Ozren Dabić <ozren.dabic@jetbrains.com>, Vladimir Poliakov <vladimir.poliakov@jetbrains.com>, Vseslav Kasatskii <vseslav.kasatskii@jetbrains.com>
6
+ Requires-Python: >=3.12
7
+ Requires-Dist: hydra-core>=1.3.2
8
+ Requires-Dist: pydantic>=2.10.6
9
+ Requires-Dist: structlog>=25.4.0
@@ -0,0 +1,22 @@
1
+ [project]
2
+ name = "idegym-common-utils"
3
+ version = "0.9.0"
4
+ description = "IdeGYM Common Utils"
5
+ authors = [
6
+ { name = "Ozren Dabić", email = "ozren.dabic@jetbrains.com" },
7
+ { name = "Vladimir Poliakov", email = "vladimir.poliakov@jetbrains.com" },
8
+ { name = "Vseslav Kasatskii", email = "vseslav.kasatskii@jetbrains.com" },
9
+ ]
10
+ requires-python = ">=3.12"
11
+ dependencies = [
12
+ "hydra-core>=1.3.2",
13
+ "pydantic>=2.10.6",
14
+ "structlog>=25.4.0",
15
+ ]
16
+
17
+ [build-system]
18
+ requires = ["hatchling"]
19
+ build-backend = "hatchling.build"
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["src/idegym"]
@@ -0,0 +1,9 @@
1
+ from importlib.metadata import PackageNotFoundError, version
2
+
3
+ try:
4
+ __version__ = version("idegym-common-utils")
5
+ except PackageNotFoundError:
6
+ __version__ = "0.0.0.dev0"
7
+
8
+ if __version__ == "0.0.0.dev0":
9
+ __version__ = "latest"
@@ -0,0 +1,8 @@
1
+ def executes_operation_in_background(func):
2
+ """Mark a FastAPI endpoint as one that executes an operation in the background.
3
+
4
+ Does nothing at runtime beyond setting the ``_executes_operation_in_background`` attribute;
5
+ acts as a marker for documentation and introspection.
6
+ """
7
+ func._executes_operation_in_background = True
8
+ return func
@@ -0,0 +1,8 @@
1
+ from collections.abc import Generator
2
+ from typing import Any
3
+
4
+
5
+ def walk(dictionary: dict[Any, Any]) -> Generator[Any, None, None]:
6
+ """Yield all leaf values from a nested dictionary, depth-first."""
7
+ for value in dictionary.values():
8
+ yield from (walk(value) if isinstance(value, dict) else [value])
@@ -0,0 +1,51 @@
1
+ from asyncio import Lock
2
+ from typing import Awaitable, Callable, Optional, ParamSpec, TypeVar, cast, overload
3
+
4
+ P = ParamSpec("P")
5
+ R = TypeVar("R")
6
+
7
+ AsyncFunction = Callable[P, Awaitable[R]]
8
+
9
+
10
+ @overload
11
+ def cached_async_result(function: AsyncFunction) -> AsyncFunction: ...
12
+
13
+
14
+ @overload
15
+ def cached_async_result() -> Callable[[AsyncFunction], AsyncFunction]: ...
16
+
17
+
18
+ def cached_async_result(target: Optional[AsyncFunction] = None):
19
+ """Cache the result of an async function after its first successful call.
20
+
21
+ Subsequent calls return the cached result without invoking the underlying function again.
22
+ Uses a double-checked locking pattern to avoid redundant awaits under concurrent callers.
23
+
24
+ Can be used with or without parentheses::
25
+
26
+ @cached_async_result
27
+ async def get_config() -> Config: ...
28
+
29
+ @cached_async_result()
30
+ async def get_config() -> Config: ...
31
+ """
32
+
33
+ def _decorate(function: AsyncFunction) -> AsyncFunction:
34
+ lock = Lock()
35
+ missing = object()
36
+ result: object = missing
37
+
38
+ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
39
+ nonlocal result
40
+ if result is not missing:
41
+ return cast(R, result)
42
+ async with lock:
43
+ if result is missing:
44
+ result = await function(*args, **kwargs)
45
+ return cast(R, result)
46
+
47
+ return wrapper
48
+
49
+ if target is None:
50
+ return _decorate
51
+ return _decorate(target)
@@ -0,0 +1,9 @@
1
+ import hashlib
2
+
3
+
4
+ def md5(value: str, /, *other: str) -> str:
5
+ """Compute the MD5 hash of the given value(s)."""
6
+ digest = hashlib.md5()
7
+ for value in (value, *other):
8
+ digest.update(value.encode())
9
+ return digest.hexdigest()
@@ -0,0 +1,13 @@
1
+ from typing import Any, Optional
2
+
3
+ from structlog import get_logger as get_named_logger
4
+ from structlog.stdlib import BoundLogger
5
+
6
+
7
+ def get_logger(name: Optional[str] = None, **initial_values: Any) -> BoundLogger:
8
+ """Return a structlog ``BoundLogger``, optionally pre-bound with context values.
9
+
10
+ Prefer this over importing structlog directly so that the project-wide
11
+ logging configuration is always the source of truth.
12
+ """
13
+ return get_named_logger(name, **initial_values)
@@ -0,0 +1,9 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def get_base_filename(file_path: str | Path) -> str:
5
+ """Return the lowercased stem of a file path, stripping all extensions.
6
+
7
+ For example, ``"Archive.tar.gz"`` returns ``"archive"``.
8
+ """
9
+ return Path(file_path).name.lower().split(".")[0]
@@ -0,0 +1,30 @@
1
+ import json
2
+ from typing import Any, Optional
3
+
4
+ from pydantic_core import PydanticSerializationError
5
+
6
+
7
+ def serialize_as_json_string(data: Any) -> Optional[str]:
8
+ """Serialize an arbitrary value to a JSON string.
9
+
10
+ Handles Pydantic v2 models via duck-typing (``model_dump_json`` preferred
11
+ over ``model_dump`` for efficiency), plain dicts/lists, bare strings, and
12
+ falls back to ``json.dumps(..., default=str)`` for other types.
13
+ Returns ``None`` if ``data`` is ``None``. On serialization failure, falls
14
+ back to ``str(data)``.
15
+ """
16
+ if data is None:
17
+ return None
18
+ try:
19
+ if hasattr(data, "model_dump_json") and callable(data.model_dump_json):
20
+ return data.model_dump_json()
21
+ elif hasattr(data, "model_dump") and callable(data.model_dump):
22
+ return json.dumps(data.model_dump())
23
+ elif isinstance(data, (dict, list)):
24
+ return json.dumps(data)
25
+ elif isinstance(data, str):
26
+ return data
27
+ else:
28
+ return json.dumps(data, default=str)
29
+ except (PydanticSerializationError, TypeError, ValueError):
30
+ return str(data)