winipedia-utils 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

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.

Potentially problematic release.


This version of winipedia-utils might be problematic. Click here for more details.

Files changed (90) hide show
  1. winipedia_utils/concurrent/concurrent.py +245 -245
  2. winipedia_utils/concurrent/multiprocessing.py +130 -130
  3. winipedia_utils/concurrent/multithreading.py +93 -93
  4. winipedia_utils/consts.py +21 -23
  5. winipedia_utils/data/__init__.py +1 -1
  6. winipedia_utils/data/dataframe/__init__.py +1 -1
  7. winipedia_utils/data/dataframe/cleaning.py +378 -378
  8. winipedia_utils/data/structures/__init__.py +1 -1
  9. winipedia_utils/data/structures/dicts.py +16 -16
  10. winipedia_utils/git/__init__.py +1 -1
  11. winipedia_utils/git/gitignore/__init__.py +1 -1
  12. winipedia_utils/git/gitignore/gitignore.py +136 -136
  13. winipedia_utils/git/pre_commit/__init__.py +1 -1
  14. winipedia_utils/git/pre_commit/config.py +70 -70
  15. winipedia_utils/git/pre_commit/hooks.py +109 -109
  16. winipedia_utils/git/pre_commit/run_hooks.py +49 -49
  17. winipedia_utils/iterating/__init__.py +1 -1
  18. winipedia_utils/iterating/iterate.py +29 -29
  19. winipedia_utils/logging/ansi.py +6 -6
  20. winipedia_utils/logging/config.py +64 -64
  21. winipedia_utils/logging/logger.py +26 -26
  22. winipedia_utils/modules/class_.py +119 -119
  23. winipedia_utils/modules/function.py +101 -101
  24. winipedia_utils/modules/module.py +379 -379
  25. winipedia_utils/modules/package.py +390 -390
  26. winipedia_utils/oop/mixins/meta.py +333 -333
  27. winipedia_utils/oop/mixins/mixin.py +37 -37
  28. winipedia_utils/os/__init__.py +1 -1
  29. winipedia_utils/os/os.py +63 -63
  30. winipedia_utils/projects/__init__.py +1 -1
  31. winipedia_utils/projects/poetry/__init__.py +1 -1
  32. winipedia_utils/projects/poetry/config.py +91 -91
  33. winipedia_utils/projects/poetry/poetry.py +31 -31
  34. winipedia_utils/projects/project.py +48 -48
  35. winipedia_utils/resources/__init__.py +1 -1
  36. winipedia_utils/resources/svgs/__init__.py +1 -1
  37. winipedia_utils/resources/svgs/download_arrow.svg +2 -2
  38. winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +5 -5
  39. winipedia_utils/resources/svgs/fullscreen_icon.svg +2 -2
  40. winipedia_utils/resources/svgs/menu_icon.svg +3 -3
  41. winipedia_utils/resources/svgs/pause_icon.svg +3 -3
  42. winipedia_utils/resources/svgs/play_icon.svg +16 -16
  43. winipedia_utils/resources/svgs/plus_icon.svg +23 -23
  44. winipedia_utils/resources/svgs/svg.py +15 -15
  45. winipedia_utils/security/__init__.py +1 -1
  46. winipedia_utils/security/cryptography.py +29 -29
  47. winipedia_utils/security/keyring.py +70 -70
  48. winipedia_utils/setup.py +47 -47
  49. winipedia_utils/testing/assertions.py +23 -23
  50. winipedia_utils/testing/convention.py +177 -177
  51. winipedia_utils/testing/create_tests.py +291 -291
  52. winipedia_utils/testing/fixtures.py +28 -28
  53. winipedia_utils/testing/tests/base/fixtures/__init__.py +1 -1
  54. winipedia_utils/testing/tests/base/fixtures/fixture.py +6 -6
  55. winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +33 -33
  56. winipedia_utils/testing/tests/base/fixtures/scopes/function.py +7 -7
  57. winipedia_utils/testing/tests/base/fixtures/scopes/module.py +31 -31
  58. winipedia_utils/testing/tests/base/fixtures/scopes/package.py +7 -7
  59. winipedia_utils/testing/tests/base/fixtures/scopes/session.py +312 -312
  60. winipedia_utils/testing/tests/base/utils/utils.py +82 -82
  61. winipedia_utils/testing/tests/conftest.py +32 -32
  62. winipedia_utils/text/string.py +126 -126
  63. {winipedia_utils-0.2.0.dist-info → winipedia_utils-0.2.1.dist-info}/METADATA +1 -4
  64. winipedia_utils-0.2.1.dist-info/RECORD +80 -0
  65. {winipedia_utils-0.2.0.dist-info → winipedia_utils-0.2.1.dist-info}/licenses/LICENSE +21 -21
  66. winipedia_utils/django/__init__.py +0 -24
  67. winipedia_utils/django/bulk.py +0 -538
  68. winipedia_utils/django/command.py +0 -334
  69. winipedia_utils/django/database.py +0 -289
  70. winipedia_utils/pyside/__init__.py +0 -1
  71. winipedia_utils/pyside/core/__init__.py +0 -1
  72. winipedia_utils/pyside/core/py_qiodevice.py +0 -476
  73. winipedia_utils/pyside/ui/__init__.py +0 -1
  74. winipedia_utils/pyside/ui/base/__init__.py +0 -1
  75. winipedia_utils/pyside/ui/base/base.py +0 -180
  76. winipedia_utils/pyside/ui/pages/__init__.py +0 -1
  77. winipedia_utils/pyside/ui/pages/base/__init__.py +0 -1
  78. winipedia_utils/pyside/ui/pages/base/base.py +0 -92
  79. winipedia_utils/pyside/ui/pages/browser.py +0 -26
  80. winipedia_utils/pyside/ui/pages/player.py +0 -85
  81. winipedia_utils/pyside/ui/widgets/__init__.py +0 -1
  82. winipedia_utils/pyside/ui/widgets/browser.py +0 -243
  83. winipedia_utils/pyside/ui/widgets/clickable_widget.py +0 -57
  84. winipedia_utils/pyside/ui/widgets/media_player.py +0 -430
  85. winipedia_utils/pyside/ui/widgets/notification.py +0 -78
  86. winipedia_utils/pyside/ui/windows/__init__.py +0 -1
  87. winipedia_utils/pyside/ui/windows/base/__init__.py +0 -1
  88. winipedia_utils/pyside/ui/windows/base/base.py +0 -49
  89. winipedia_utils-0.2.0.dist-info/RECORD +0 -103
  90. {winipedia_utils-0.2.0.dist-info → winipedia_utils-0.2.1.dist-info}/WHEEL +0 -0
@@ -1,379 +1,379 @@
1
- """Module utilities for introspection and manipulation.
2
-
3
- This module provides comprehensive utility functions for working with Python modules,
4
- including path conversions, module creation, object importing, and content extraction.
5
- It handles the complexities of Python's module system by providing a consistent API
6
- for module operations across different contexts (packages vs. standalone modules).
7
-
8
- The utilities support both runtime module manipulation and static analysis,
9
- making them suitable for code generation, testing frameworks, and dynamic imports.
10
- """
11
-
12
- import inspect
13
- import os
14
- import sys
15
- import time
16
- from collections.abc import Callable, Sequence
17
- from importlib import import_module
18
- from pathlib import Path
19
- from types import ModuleType
20
- from typing import Any, cast
21
-
22
- from winipedia_utils.logging.logger import get_logger
23
- from winipedia_utils.modules.class_ import (
24
- get_all_cls_from_module,
25
- get_all_methods_from_cls,
26
- )
27
- from winipedia_utils.modules.function import get_all_functions_from_module
28
- from winipedia_utils.modules.package import (
29
- get_modules_and_packages_from_package,
30
- make_dir_with_init_file,
31
- module_is_package,
32
- )
33
-
34
- logger = get_logger(__name__)
35
-
36
-
37
- def get_module_content_as_str(module: ModuleType) -> str:
38
- """Retrieve the complete source code of a module as a string.
39
-
40
- This function locates the physical file associated with the given module object
41
- and reads its entire content. It works for both regular modules and packages
42
- by determining the correct path using module_to_path.
43
-
44
- Args:
45
- module: The module object whose source code should be retrieved
46
-
47
-
48
- Returns:
49
- The complete source code of the module as a string
50
-
51
- """
52
- path = to_path(module, is_package=False)
53
- return path.read_text()
54
-
55
-
56
- def to_module_name(path: str | Path | ModuleType) -> str:
57
- """Convert a filesystem path to a Python module import name.
58
-
59
- Transforms a file or directory path into the corresponding Python module name
60
- by making it relative to the current directory, removing the file extension,
61
- and replacing directory separators with dots.
62
-
63
- Args:
64
- path: a str that represents a path or a Path object or a ModuleType object
65
- or a str that represents a module name
66
-
67
- Returns:
68
- The Python module name corresponding to the path
69
-
70
- Example:
71
- path_to_module_name("src/package/module.py") -> "src.package.module"
72
-
73
- """
74
- if isinstance(path, ModuleType):
75
- return path.__name__
76
- if isinstance(path, Path):
77
- abs_path = path.resolve()
78
- rel_path = abs_path.relative_to(Path.cwd())
79
- if rel_path.suffix:
80
- rel_path = rel_path.with_suffix("")
81
- # return joined on . parts
82
- return ".".join(rel_path.parts)
83
- if path in (".", "./", ""):
84
- return ""
85
- # we get a str that can either be a dotted module name or a path
86
- # e.g. package/module.py or package/module or
87
- # package.module or just package/package2
88
- # or just package with nothing
89
- path = path.removesuffix(".py")
90
- if "." in path:
91
- # already a module name
92
- return path
93
- return to_module_name(Path(path))
94
-
95
-
96
- def to_path(module_name: str | ModuleType | Path, *, is_package: bool) -> Path:
97
- """Convert a Python module import name to its filesystem path.
98
-
99
- Transforms a Python module name into the corresponding file path by replacing
100
- dots with directory separators and adding the .py extension. Uses the
101
- package_name_to_path function for the directory structure.
102
-
103
- Args:
104
- module_name: A Python module name to convert or Path or ModuleType
105
- is_package: Whether to return the path to the package directory
106
- without the .py extension
107
-
108
- Returns:
109
- A Path object representing the filesystem path to the module
110
- if is_package is True, returns the path to the package directory
111
- without the .py extension
112
-
113
- Example:
114
- module_name_to_path("src.package.module") -> Path("src/package/module.py")
115
-
116
- """
117
- module_name = to_module_name(module_name)
118
- path = Path(module_name.replace(".", os.sep))
119
- # for smth like pyinstaller we support frozen path
120
- if getattr(sys, "frozen", False):
121
- path = Path(getattr(sys, "_MEIPASS", "")) / path
122
- if is_package:
123
- return path
124
- return path.with_suffix(".py")
125
-
126
-
127
- def create_module(
128
- module_name: str | Path | ModuleType, *, is_package: bool
129
- ) -> ModuleType:
130
- """Create a new Python module file and import it.
131
-
132
- Creates a new module file at the location specified by the module name,
133
- ensuring all necessary parent directories and __init__.py files exist.
134
- Optionally writes content to the module file and parent __init__.py files.
135
- Finally imports and returns the newly created module.
136
-
137
- Args:
138
- module_name: The fully qualified name of the module to create
139
- is_package: Whether to create a package instead of a module
140
-
141
- Returns:
142
- The imported module object representing the newly created module
143
-
144
- Note:
145
- Includes a small delay (0.1s) before importing to ensure filesystem operations
146
- are complete, preventing race conditions.
147
-
148
- """
149
- path = to_path(module_name, is_package=is_package)
150
- if path == Path():
151
- msg = f"Cannot create module {module_name=} because it is the current directory"
152
- logger.error(msg)
153
- raise ValueError(msg)
154
-
155
- make_dir_with_init_file(path if is_package else path.parent)
156
- # create the module file if not exists
157
- if not path.exists() and not is_package:
158
- path.write_text(get_default_module_content())
159
-
160
- module_name = to_module_name(path)
161
- # wait before importing the module
162
- time.sleep(0.1)
163
- return import_module(module_name)
164
-
165
-
166
- def make_obj_importpath(obj: Callable[..., Any] | type | ModuleType) -> str:
167
- """Create a fully qualified import path string for a Python object.
168
-
169
- Generates the import path that would be used to import the given object.
170
- Handles different types of objects (modules, classes, functions) appropriately.
171
-
172
- Args:
173
- obj: The object (module, class, or function) to create an import path for
174
-
175
- Returns:
176
- A string representing the fully qualified import path for the object
177
-
178
- Examples:
179
- For a module: "package.subpackage.module"
180
- For a class: "package.module.ClassName"
181
- For a function: "package.module.function_name"
182
- For a method: "package.module.ClassName.method_name"
183
-
184
- """
185
- if isinstance(obj, ModuleType):
186
- return obj.__name__
187
- module: str | None = get_module_of_obj(obj).__name__
188
- obj_name = get_qualname_of_obj(obj)
189
- if not module:
190
- return obj_name
191
- return module + "." + obj_name
192
-
193
-
194
- def import_obj_from_importpath(
195
- importpath: str,
196
- ) -> Callable[..., Any] | type | ModuleType:
197
- """Import a Python object (module, class, or function) from its import path.
198
-
199
- Attempts to import the object specified by the given import path. First tries
200
- to import it as a module, and if that fails, attempts to import it as a class
201
- or function by splitting the path and using getattr.
202
-
203
- Args:
204
- importpath: The fully qualified import path of the object
205
-
206
- Returns:
207
- The imported object (module, class, or function)
208
-
209
- Raises:
210
- ImportError: If the module part of the path cannot be imported
211
- AttributeError: If the object is not found in the module
212
-
213
- """
214
- try:
215
- return import_module(importpath)
216
- except ImportError:
217
- # might be a class or function
218
- module_name, obj_name = importpath.rsplit(".", 1)
219
- module = import_module(module_name)
220
- obj: Callable[..., Any] | type | ModuleType = getattr(module, obj_name)
221
- return obj
222
-
223
-
224
- def get_isolated_obj_name(obj: Callable[..., Any] | type | ModuleType) -> str:
225
- """Extract the bare name of an object without its module prefix.
226
-
227
- Retrieves just the name part of an object, without any module path information.
228
- For modules, returns the last component of the module path.
229
-
230
- Args:
231
- obj: The object (module, class, or function) to get the name for
232
-
233
- Returns:
234
- The isolated name of the object without any module path
235
-
236
- Examples:
237
- For a module "package.subpackage.module": returns "module"
238
- For a class: returns the class name
239
- For a function: returns the function name
240
-
241
- """
242
- obj = get_unwrapped_obj(obj)
243
- if isinstance(obj, ModuleType):
244
- return obj.__name__.split(".")[-1]
245
- if isinstance(obj, type):
246
- return obj.__name__
247
- return get_qualname_of_obj(obj).split(".")[-1]
248
-
249
-
250
- def get_objs_from_obj(
251
- obj: Callable[..., Any] | type | ModuleType,
252
- ) -> Sequence[Callable[..., Any] | type | ModuleType]:
253
- """Extract all contained objects from a container object.
254
-
255
- Retrieves all relevant objects contained within the given object, with behavior
256
- depending on the type of the container:
257
- - For modules: returns all functions and classes defined in the module
258
- - For packages: returns all submodules in the package
259
- - For classes: returns all methods defined directly in the class
260
- - For other objects: returns an empty list
261
-
262
- Args:
263
- obj: The container object to extract contained objects from
264
-
265
- Returns:
266
- A sequence of objects contained within the given container object
267
-
268
- """
269
- if isinstance(obj, ModuleType):
270
- if module_is_package(obj):
271
- return get_modules_and_packages_from_package(obj)[1]
272
- objs: list[Callable[..., Any] | type] = []
273
- objs.extend(get_all_functions_from_module(obj))
274
- objs.extend(get_all_cls_from_module(obj))
275
- return objs
276
- if isinstance(obj, type):
277
- return get_all_methods_from_cls(obj, exclude_parent_methods=True)
278
- return []
279
-
280
-
281
- def execute_all_functions_from_module(module: ModuleType) -> list[Any]:
282
- """Execute all functions defined in a module with no arguments.
283
-
284
- Retrieves all functions defined in the module and calls each one with no arguments.
285
- Collects and returns the results of all function calls.
286
-
287
- Args:
288
- module: The module containing functions to execute
289
-
290
- Returns:
291
- A list containing the return values from all executed functions
292
-
293
- Note:
294
- Only executes functions defined directly in the module, not imported functions.
295
- All functions must accept being called with no arguments.
296
-
297
- """
298
- return [f() for f in get_all_functions_from_module(module)]
299
-
300
-
301
- def get_default_init_module_content() -> str:
302
- """Generate standardized content for an __init__.py file.
303
-
304
- Creates a simple docstring for an __init__.py file based on its location,
305
- following the project's documentation conventions.
306
-
307
- Args:
308
- path: The path to the __init__.py file or its parent directory
309
-
310
- Returns:
311
- A string containing a properly formatted docstring for the __init__.py file
312
-
313
- """
314
- return '''"""__init__ module."""'''
315
-
316
-
317
- def get_default_module_content() -> str:
318
- """Generate standardized content for a Python module file.
319
-
320
- Creates a simple docstring for a module file based on its name,
321
- following the project's documentation conventions.
322
-
323
- Returns:
324
- A string containing a properly formatted docstring for the module file
325
-
326
- """
327
- return '''"""module."""'''
328
-
329
-
330
- def inside_frozen_bundle() -> bool:
331
- """Return True if the code is running inside a frozen bundle."""
332
- return getattr(sys, "frozen", False)
333
-
334
-
335
- def get_def_line(obj: Any) -> int:
336
- """Return the line number where a method-like object is defined."""
337
- if isinstance(obj, property):
338
- obj = obj.fget
339
- unwrapped = inspect.unwrap(obj)
340
- if hasattr(unwrapped, "__code__"):
341
- return int(unwrapped.__code__.co_firstlineno)
342
- # getsourcelines does not work if in a pyinstaller bundle or something
343
- if inside_frozen_bundle():
344
- return 0
345
- return inspect.getsourcelines(unwrapped)[1]
346
-
347
-
348
- def get_module_of_obj(obj: Any, default: ModuleType | None = None) -> ModuleType:
349
- """Return the module name where a method-like object is defined.
350
-
351
- Args:
352
- obj: Method-like object (funcs, method, property, staticmethod, classmethod...)
353
- default: Default module to return if the module cannot be determined
354
-
355
- Returns:
356
- The module name as a string, or None if module cannot be determined.
357
-
358
- """
359
- unwrapped = get_unwrapped_obj(obj)
360
- module = inspect.getmodule(unwrapped)
361
- if not module:
362
- msg = f"Could not determine module of {obj}"
363
- if default:
364
- return default
365
- raise ValueError(msg)
366
- return module
367
-
368
-
369
- def get_qualname_of_obj(obj: Callable[..., Any] | type) -> str:
370
- """Return the name of a method-like object."""
371
- unwrapped = get_unwrapped_obj(obj)
372
- return cast("str", unwrapped.__qualname__)
373
-
374
-
375
- def get_unwrapped_obj(obj: Any) -> Any:
376
- """Return the unwrapped version of a method-like object."""
377
- if isinstance(obj, property):
378
- obj = obj.fget # get the getter function of the property
379
- return inspect.unwrap(obj)
1
+ """Module utilities for introspection and manipulation.
2
+
3
+ This module provides comprehensive utility functions for working with Python modules,
4
+ including path conversions, module creation, object importing, and content extraction.
5
+ It handles the complexities of Python's module system by providing a consistent API
6
+ for module operations across different contexts (packages vs. standalone modules).
7
+
8
+ The utilities support both runtime module manipulation and static analysis,
9
+ making them suitable for code generation, testing frameworks, and dynamic imports.
10
+ """
11
+
12
+ import inspect
13
+ import os
14
+ import sys
15
+ import time
16
+ from collections.abc import Callable, Sequence
17
+ from importlib import import_module
18
+ from pathlib import Path
19
+ from types import ModuleType
20
+ from typing import Any, cast
21
+
22
+ from winipedia_utils.logging.logger import get_logger
23
+ from winipedia_utils.modules.class_ import (
24
+ get_all_cls_from_module,
25
+ get_all_methods_from_cls,
26
+ )
27
+ from winipedia_utils.modules.function import get_all_functions_from_module
28
+ from winipedia_utils.modules.package import (
29
+ get_modules_and_packages_from_package,
30
+ make_dir_with_init_file,
31
+ module_is_package,
32
+ )
33
+
34
+ logger = get_logger(__name__)
35
+
36
+
37
+ def get_module_content_as_str(module: ModuleType) -> str:
38
+ """Retrieve the complete source code of a module as a string.
39
+
40
+ This function locates the physical file associated with the given module object
41
+ and reads its entire content. It works for both regular modules and packages
42
+ by determining the correct path using module_to_path.
43
+
44
+ Args:
45
+ module: The module object whose source code should be retrieved
46
+
47
+
48
+ Returns:
49
+ The complete source code of the module as a string
50
+
51
+ """
52
+ path = to_path(module, is_package=False)
53
+ return path.read_text()
54
+
55
+
56
+ def to_module_name(path: str | Path | ModuleType) -> str:
57
+ """Convert a filesystem path to a Python module import name.
58
+
59
+ Transforms a file or directory path into the corresponding Python module name
60
+ by making it relative to the current directory, removing the file extension,
61
+ and replacing directory separators with dots.
62
+
63
+ Args:
64
+ path: a str that represents a path or a Path object or a ModuleType object
65
+ or a str that represents a module name
66
+
67
+ Returns:
68
+ The Python module name corresponding to the path
69
+
70
+ Example:
71
+ path_to_module_name("src/package/module.py") -> "src.package.module"
72
+
73
+ """
74
+ if isinstance(path, ModuleType):
75
+ return path.__name__
76
+ if isinstance(path, Path):
77
+ abs_path = path.resolve()
78
+ rel_path = abs_path.relative_to(Path.cwd())
79
+ if rel_path.suffix:
80
+ rel_path = rel_path.with_suffix("")
81
+ # return joined on . parts
82
+ return ".".join(rel_path.parts)
83
+ if path in (".", "./", ""):
84
+ return ""
85
+ # we get a str that can either be a dotted module name or a path
86
+ # e.g. package/module.py or package/module or
87
+ # package.module or just package/package2
88
+ # or just package with nothing
89
+ path = path.removesuffix(".py")
90
+ if "." in path:
91
+ # already a module name
92
+ return path
93
+ return to_module_name(Path(path))
94
+
95
+
96
+ def to_path(module_name: str | ModuleType | Path, *, is_package: bool) -> Path:
97
+ """Convert a Python module import name to its filesystem path.
98
+
99
+ Transforms a Python module name into the corresponding file path by replacing
100
+ dots with directory separators and adding the .py extension. Uses the
101
+ package_name_to_path function for the directory structure.
102
+
103
+ Args:
104
+ module_name: A Python module name to convert or Path or ModuleType
105
+ is_package: Whether to return the path to the package directory
106
+ without the .py extension
107
+
108
+ Returns:
109
+ A Path object representing the filesystem path to the module
110
+ if is_package is True, returns the path to the package directory
111
+ without the .py extension
112
+
113
+ Example:
114
+ module_name_to_path("src.package.module") -> Path("src/package/module.py")
115
+
116
+ """
117
+ module_name = to_module_name(module_name)
118
+ path = Path(module_name.replace(".", os.sep))
119
+ # for smth like pyinstaller we support frozen path
120
+ if getattr(sys, "frozen", False):
121
+ path = Path(getattr(sys, "_MEIPASS", "")) / path
122
+ if is_package:
123
+ return path
124
+ return path.with_suffix(".py")
125
+
126
+
127
+ def create_module(
128
+ module_name: str | Path | ModuleType, *, is_package: bool
129
+ ) -> ModuleType:
130
+ """Create a new Python module file and import it.
131
+
132
+ Creates a new module file at the location specified by the module name,
133
+ ensuring all necessary parent directories and __init__.py files exist.
134
+ Optionally writes content to the module file and parent __init__.py files.
135
+ Finally imports and returns the newly created module.
136
+
137
+ Args:
138
+ module_name: The fully qualified name of the module to create
139
+ is_package: Whether to create a package instead of a module
140
+
141
+ Returns:
142
+ The imported module object representing the newly created module
143
+
144
+ Note:
145
+ Includes a small delay (0.1s) before importing to ensure filesystem operations
146
+ are complete, preventing race conditions.
147
+
148
+ """
149
+ path = to_path(module_name, is_package=is_package)
150
+ if path == Path():
151
+ msg = f"Cannot create module {module_name=} because it is the current directory"
152
+ logger.error(msg)
153
+ raise ValueError(msg)
154
+
155
+ make_dir_with_init_file(path if is_package else path.parent)
156
+ # create the module file if not exists
157
+ if not path.exists() and not is_package:
158
+ path.write_text(get_default_module_content())
159
+
160
+ module_name = to_module_name(path)
161
+ # wait before importing the module
162
+ time.sleep(0.1)
163
+ return import_module(module_name)
164
+
165
+
166
+ def make_obj_importpath(obj: Callable[..., Any] | type | ModuleType) -> str:
167
+ """Create a fully qualified import path string for a Python object.
168
+
169
+ Generates the import path that would be used to import the given object.
170
+ Handles different types of objects (modules, classes, functions) appropriately.
171
+
172
+ Args:
173
+ obj: The object (module, class, or function) to create an import path for
174
+
175
+ Returns:
176
+ A string representing the fully qualified import path for the object
177
+
178
+ Examples:
179
+ For a module: "package.subpackage.module"
180
+ For a class: "package.module.ClassName"
181
+ For a function: "package.module.function_name"
182
+ For a method: "package.module.ClassName.method_name"
183
+
184
+ """
185
+ if isinstance(obj, ModuleType):
186
+ return obj.__name__
187
+ module: str | None = get_module_of_obj(obj).__name__
188
+ obj_name = get_qualname_of_obj(obj)
189
+ if not module:
190
+ return obj_name
191
+ return module + "." + obj_name
192
+
193
+
194
+ def import_obj_from_importpath(
195
+ importpath: str,
196
+ ) -> Callable[..., Any] | type | ModuleType:
197
+ """Import a Python object (module, class, or function) from its import path.
198
+
199
+ Attempts to import the object specified by the given import path. First tries
200
+ to import it as a module, and if that fails, attempts to import it as a class
201
+ or function by splitting the path and using getattr.
202
+
203
+ Args:
204
+ importpath: The fully qualified import path of the object
205
+
206
+ Returns:
207
+ The imported object (module, class, or function)
208
+
209
+ Raises:
210
+ ImportError: If the module part of the path cannot be imported
211
+ AttributeError: If the object is not found in the module
212
+
213
+ """
214
+ try:
215
+ return import_module(importpath)
216
+ except ImportError:
217
+ # might be a class or function
218
+ module_name, obj_name = importpath.rsplit(".", 1)
219
+ module = import_module(module_name)
220
+ obj: Callable[..., Any] | type | ModuleType = getattr(module, obj_name)
221
+ return obj
222
+
223
+
224
+ def get_isolated_obj_name(obj: Callable[..., Any] | type | ModuleType) -> str:
225
+ """Extract the bare name of an object without its module prefix.
226
+
227
+ Retrieves just the name part of an object, without any module path information.
228
+ For modules, returns the last component of the module path.
229
+
230
+ Args:
231
+ obj: The object (module, class, or function) to get the name for
232
+
233
+ Returns:
234
+ The isolated name of the object without any module path
235
+
236
+ Examples:
237
+ For a module "package.subpackage.module": returns "module"
238
+ For a class: returns the class name
239
+ For a function: returns the function name
240
+
241
+ """
242
+ obj = get_unwrapped_obj(obj)
243
+ if isinstance(obj, ModuleType):
244
+ return obj.__name__.split(".")[-1]
245
+ if isinstance(obj, type):
246
+ return obj.__name__
247
+ return get_qualname_of_obj(obj).split(".")[-1]
248
+
249
+
250
+ def get_objs_from_obj(
251
+ obj: Callable[..., Any] | type | ModuleType,
252
+ ) -> Sequence[Callable[..., Any] | type | ModuleType]:
253
+ """Extract all contained objects from a container object.
254
+
255
+ Retrieves all relevant objects contained within the given object, with behavior
256
+ depending on the type of the container:
257
+ - For modules: returns all functions and classes defined in the module
258
+ - For packages: returns all submodules in the package
259
+ - For classes: returns all methods defined directly in the class
260
+ - For other objects: returns an empty list
261
+
262
+ Args:
263
+ obj: The container object to extract contained objects from
264
+
265
+ Returns:
266
+ A sequence of objects contained within the given container object
267
+
268
+ """
269
+ if isinstance(obj, ModuleType):
270
+ if module_is_package(obj):
271
+ return get_modules_and_packages_from_package(obj)[1]
272
+ objs: list[Callable[..., Any] | type] = []
273
+ objs.extend(get_all_functions_from_module(obj))
274
+ objs.extend(get_all_cls_from_module(obj))
275
+ return objs
276
+ if isinstance(obj, type):
277
+ return get_all_methods_from_cls(obj, exclude_parent_methods=True)
278
+ return []
279
+
280
+
281
+ def execute_all_functions_from_module(module: ModuleType) -> list[Any]:
282
+ """Execute all functions defined in a module with no arguments.
283
+
284
+ Retrieves all functions defined in the module and calls each one with no arguments.
285
+ Collects and returns the results of all function calls.
286
+
287
+ Args:
288
+ module: The module containing functions to execute
289
+
290
+ Returns:
291
+ A list containing the return values from all executed functions
292
+
293
+ Note:
294
+ Only executes functions defined directly in the module, not imported functions.
295
+ All functions must accept being called with no arguments.
296
+
297
+ """
298
+ return [f() for f in get_all_functions_from_module(module)]
299
+
300
+
301
+ def get_default_init_module_content() -> str:
302
+ """Generate standardized content for an __init__.py file.
303
+
304
+ Creates a simple docstring for an __init__.py file based on its location,
305
+ following the project's documentation conventions.
306
+
307
+ Args:
308
+ path: The path to the __init__.py file or its parent directory
309
+
310
+ Returns:
311
+ A string containing a properly formatted docstring for the __init__.py file
312
+
313
+ """
314
+ return '''"""__init__ module."""'''
315
+
316
+
317
+ def get_default_module_content() -> str:
318
+ """Generate standardized content for a Python module file.
319
+
320
+ Creates a simple docstring for a module file based on its name,
321
+ following the project's documentation conventions.
322
+
323
+ Returns:
324
+ A string containing a properly formatted docstring for the module file
325
+
326
+ """
327
+ return '''"""module."""'''
328
+
329
+
330
+ def inside_frozen_bundle() -> bool:
331
+ """Return True if the code is running inside a frozen bundle."""
332
+ return getattr(sys, "frozen", False)
333
+
334
+
335
+ def get_def_line(obj: Any) -> int:
336
+ """Return the line number where a method-like object is defined."""
337
+ if isinstance(obj, property):
338
+ obj = obj.fget
339
+ unwrapped = inspect.unwrap(obj)
340
+ if hasattr(unwrapped, "__code__"):
341
+ return int(unwrapped.__code__.co_firstlineno)
342
+ # getsourcelines does not work if in a pyinstaller bundle or something
343
+ if inside_frozen_bundle():
344
+ return 0
345
+ return inspect.getsourcelines(unwrapped)[1]
346
+
347
+
348
+ def get_module_of_obj(obj: Any, default: ModuleType | None = None) -> ModuleType:
349
+ """Return the module name where a method-like object is defined.
350
+
351
+ Args:
352
+ obj: Method-like object (funcs, method, property, staticmethod, classmethod...)
353
+ default: Default module to return if the module cannot be determined
354
+
355
+ Returns:
356
+ The module name as a string, or None if module cannot be determined.
357
+
358
+ """
359
+ unwrapped = get_unwrapped_obj(obj)
360
+ module = inspect.getmodule(unwrapped)
361
+ if not module:
362
+ msg = f"Could not determine module of {obj}"
363
+ if default:
364
+ return default
365
+ raise ValueError(msg)
366
+ return module
367
+
368
+
369
+ def get_qualname_of_obj(obj: Callable[..., Any] | type) -> str:
370
+ """Return the name of a method-like object."""
371
+ unwrapped = get_unwrapped_obj(obj)
372
+ return cast("str", unwrapped.__qualname__)
373
+
374
+
375
+ def get_unwrapped_obj(obj: Any) -> Any:
376
+ """Return the unwrapped version of a method-like object."""
377
+ if isinstance(obj, property):
378
+ obj = obj.fget # get the getter function of the property
379
+ return inspect.unwrap(obj)