winipedia-utils 0.4.56__tar.gz → 0.5.18__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.

Potentially problematic release.


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

Files changed (96) hide show
  1. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/PKG-INFO +1 -1
  2. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/pyproject.toml +1 -1
  3. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/workflows/base/base.py +55 -20
  4. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/workflows/health_check.py +25 -3
  5. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/workflows/release.py +9 -3
  6. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/pre_commit/config.py +1 -1
  7. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/pre_commit/hooks.py +17 -19
  8. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/pre_commit/run_hooks.py +15 -3
  9. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/modules/class_.py +11 -4
  10. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/modules/function.py +9 -3
  11. winipedia_utils-0.5.18/winipedia_utils/modules/inspection.py +56 -0
  12. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/modules/module.py +2 -32
  13. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/projects/poetry/config.py +35 -16
  14. winipedia_utils-0.5.18/winipedia_utils/projects/poetry/poetry.py +248 -0
  15. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/create_tests.py +1 -1
  16. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/text/config.py +3 -3
  17. winipedia_utils-0.4.56/winipedia_utils/projects/poetry/poetry.py +0 -129
  18. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/LICENSE +0 -0
  19. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/README.md +0 -0
  20. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/__init__.py +0 -0
  21. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/concurrent/__init__.py +0 -0
  22. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/concurrent/concurrent.py +0 -0
  23. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/concurrent/multiprocessing.py +0 -0
  24. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/concurrent/multithreading.py +0 -0
  25. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/data/__init__.py +0 -0
  26. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/data/dataframe/__init__.py +0 -0
  27. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/data/dataframe/cleaning.py +0 -0
  28. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/data/structures/__init__.py +0 -0
  29. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/data/structures/dicts.py +0 -0
  30. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/__init__.py +0 -0
  31. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/__init__.py +0 -0
  32. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/github.py +0 -0
  33. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/repo/__init__.py +0 -0
  34. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/repo/protect.py +0 -0
  35. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/repo/repo.py +0 -0
  36. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/workflows/__init__.py +0 -0
  37. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/workflows/base/__init__.py +0 -0
  38. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/github/workflows/publish.py +0 -0
  39. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/gitignore/__init__.py +0 -0
  40. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/gitignore/config.py +0 -0
  41. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/gitignore/gitignore.py +0 -0
  42. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/git/pre_commit/__init__.py +0 -0
  43. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/iterating/__init__.py +0 -0
  44. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/iterating/iterate.py +0 -0
  45. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/logging/__init__.py +0 -0
  46. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/logging/ansi.py +0 -0
  47. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/logging/config.py +0 -0
  48. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/logging/logger.py +0 -0
  49. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/modules/__init__.py +0 -0
  50. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/modules/package.py +0 -0
  51. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/oop/__init__.py +0 -0
  52. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/oop/mixins/__init__.py +0 -0
  53. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/oop/mixins/meta.py +0 -0
  54. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/oop/mixins/mixin.py +0 -0
  55. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/os/__init__.py +0 -0
  56. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/os/os.py +0 -0
  57. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/projects/__init__.py +0 -0
  58. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/projects/poetry/__init__.py +0 -0
  59. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/projects/project.py +0 -0
  60. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/py.typed +0 -0
  61. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/__init__.py +0 -0
  62. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/__init__.py +0 -0
  63. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/delete_garbage_can.svg +0 -0
  64. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/download_arrow.svg +0 -0
  65. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +0 -0
  66. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/fullscreen_icon.svg +0 -0
  67. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/menu_icon.svg +0 -0
  68. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/pause_icon.svg +0 -0
  69. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/play_icon.svg +0 -0
  70. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/plus_icon.svg +0 -0
  71. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/resources/svgs/svg.py +0 -0
  72. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/security/__init__.py +0 -0
  73. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/security/cryptography.py +0 -0
  74. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/security/keyring.py +0 -0
  75. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/setup.py +0 -0
  76. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/__init__.py +0 -0
  77. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/assertions.py +0 -0
  78. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/config.py +0 -0
  79. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/convention.py +0 -0
  80. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/fixtures.py +0 -0
  81. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/skip.py +0 -0
  82. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/__init__.py +0 -0
  83. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/__init__.py +0 -0
  84. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/__init__.py +0 -0
  85. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/fixture.py +0 -0
  86. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/scopes/__init__.py +0 -0
  87. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +0 -0
  88. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/scopes/function.py +0 -0
  89. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/scopes/module.py +0 -0
  90. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/scopes/package.py +0 -0
  91. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/fixtures/scopes/session.py +0 -0
  92. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/utils/__init__.py +0 -0
  93. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/base/utils/utils.py +0 -0
  94. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/testing/tests/conftest.py +0 -0
  95. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/text/__init__.py +0 -0
  96. {winipedia_utils-0.4.56 → winipedia_utils-0.5.18}/winipedia_utils/text/string.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: winipedia-utils
3
- Version: 0.4.56
3
+ Version: 0.5.18
4
4
  Summary: A package with many utility functions
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1,7 +1,7 @@
1
1
  # Project section
2
2
  [project]
3
3
  name = "winipedia-utils"
4
- version = "0.4.56"
4
+ version = "0.5.18"
5
5
  description = "A package with many utility functions"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.12"
@@ -74,36 +74,46 @@ class Workflow(YamlConfigFile):
74
74
  "on": cls.get_workflow_triggers(),
75
75
  "permissions": cls.get_permissions(),
76
76
  "run-name": cls.get_run_name(),
77
+ "defaults": {"run": {"shell": "bash"}},
77
78
  "jobs": cls.get_jobs(),
78
79
  }
79
80
 
80
81
  @classmethod
81
- def get_standard_job(
82
+ def get_standard_job( # noqa: PLR0913
82
83
  cls,
83
84
  name: str | None = None,
84
85
  runs_on: str = "ubuntu-latest",
86
+ strategy: dict[str, Any] | None = None,
85
87
  permissions: dict[str, Any] | None = None,
86
88
  if_condition: str | None = None,
87
89
  steps: list[dict[str, Any]] | None = None,
90
+ needs: list[str] | None = None,
88
91
  ) -> dict[str, Any]:
89
92
  """Get a standard job."""
93
+ job: dict[str, Any] = {}
90
94
  if name is None:
91
95
  name = cls.get_filename()
96
+ job[name] = {}
97
+ job_config = job[name]
92
98
 
93
- if steps is None:
94
- steps = []
95
-
96
- job: dict[str, Any] = {
97
- name: {
98
- "runs-on": runs_on,
99
- "steps": steps,
100
- }
101
- }
102
99
  if permissions is not None:
103
- job[name]["permissions"] = permissions
100
+ job_config["permissions"] = permissions
101
+
102
+ if strategy is not None:
103
+ job_config["strategy"] = strategy
104
+
105
+ job_config["runs-on"] = runs_on
106
+
107
+ if needs is not None:
108
+ job_config["needs"] = needs
104
109
 
105
110
  if if_condition is not None:
106
- job[name]["if"] = if_condition
111
+ job_config["if"] = if_condition
112
+
113
+ if steps is None:
114
+ steps = []
115
+ job_config["steps"] = steps
116
+
107
117
  return job
108
118
 
109
119
  @classmethod
@@ -151,8 +161,9 @@ class Workflow(YamlConfigFile):
151
161
  fetch_depth: int | None = None,
152
162
  configure_pipy_token: bool = False,
153
163
  force_main_head: bool = False,
154
- token: bool = False,
164
+ repo_token: bool = False,
155
165
  with_keyring: bool = False,
166
+ strategy_matrix: bool = False,
156
167
  ) -> list[dict[str, Any]]:
157
168
  """Get the poetry steps.
158
169
 
@@ -163,13 +174,15 @@ class Workflow(YamlConfigFile):
163
174
  force_main_head: Whether to exit if the running branch or current commit is not
164
175
  equal to the most recent commit on main. This is useful for workflows that
165
176
  should only run on main.
166
- token: Whether to use the repository token.
177
+ repo_token: Whether to use the repository token.
167
178
  with_keyring: Whether to setup the keyring.
179
+ strategy_matrix: Whether to use the strategy matrix python-version.
180
+ This is useful for jobs that use a matrix.
168
181
 
169
182
  Returns:
170
183
  The poetry steps.
171
184
  """
172
- steps = [cls.get_checkout_step(fetch_depth, token=token)]
185
+ steps = [cls.get_checkout_step(fetch_depth, token=repo_token)]
173
186
  if force_main_head:
174
187
  # exit with code 1 if the running branch is not main
175
188
  steps.append(
@@ -184,21 +197,33 @@ class Workflow(YamlConfigFile):
184
197
  "name": "Setup Python",
185
198
  "uses": "actions/setup-python@main",
186
199
  "with": {
187
- "python-version": PyprojectConfigFile.get_latest_possible_python_version() # noqa: E501
200
+ # get latest if strategy matrix python-version is not set
201
+ "python-version": "${{ matrix.python-version }}"
202
+ if strategy_matrix
203
+ else str(PyprojectConfigFile.get_latest_possible_python_version())
188
204
  },
189
205
  }
190
206
  )
191
207
  steps.append(
192
208
  {
193
- "name": "Install Poetry",
194
- "run": "curl -sSL https://install.python-poetry.org | python3 -",
209
+ "name": "Setup Poetry",
210
+ "uses": "snok/install-poetry@main",
195
211
  }
196
212
  )
197
213
 
214
+ if strategy_matrix:
215
+ steps.append(
216
+ {
217
+ # windows needs this step to find poetry
218
+ "name": "Add Poetry to PATH",
219
+ "run": "echo 'C:/Users/runneradmin/.local/bin' >> $GITHUB_PATH",
220
+ }
221
+ )
222
+
198
223
  if configure_pipy_token:
199
224
  steps.append(
200
225
  {
201
- "name": "Configure Poetry",
226
+ "name": "Configure Poetry with PyPI Token",
202
227
  "run": "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}",
203
228
  }
204
229
  )
@@ -215,6 +240,13 @@ class Workflow(YamlConfigFile):
215
240
  def get_release_steps(cls) -> list[dict[str, Any]]:
216
241
  """Get the release steps."""
217
242
  return [
243
+ *cls.get_poetry_setup_steps(
244
+ install_dependencies=True,
245
+ repo_token=True,
246
+ ),
247
+ cls.get_pre_commit_step(),
248
+ cls.get_commit_step(),
249
+ cls.get_extract_version_step(),
218
250
  {
219
251
  "name": "Tag and Push",
220
252
  "run": f"git push && git tag {cls.get_version()} && git push origin {cls.get_version()}", # noqa: E501
@@ -260,6 +292,9 @@ class Workflow(YamlConfigFile):
260
292
  """
261
293
  step: dict[str, Any] = {
262
294
  "name": "Run Hooks",
295
+ # poetry run is necessary although the hook itself uses poetry run as well.
296
+ # not sure why, but on windows-latest the venv is not continued to the hooks
297
+ # and if you leave it here then pre-commit command is not found
263
298
  "run": "poetry run pre-commit run --all-files --verbose",
264
299
  }
265
300
  if get_src_package() == winipedia_utils:
@@ -287,7 +322,7 @@ class Workflow(YamlConfigFile):
287
322
  """Get the setup keyring step."""
288
323
  return {
289
324
  "name": "Setup CI keyring",
290
- "run": """poetry run pip install keyrings.alt && poetry run python -c "import keyring; from keyrings.alt.file import PlaintextKeyring; keyring.set_keyring(PlaintextKeyring()); keyring.set_password('video_vault','ci_user','ci-secret-token'); print('Keyring OK:', keyring.get_password('video_vault','ci_user'))" """, # noqa: E501
325
+ "run": 'poetry run pip install keyrings.alt && poetry run python -c "import keyring; from keyrings.alt.file import PlaintextKeyring; keyring.set_keyring(PlaintextKeyring());"', # noqa: E501
291
326
  }
292
327
 
293
328
  @classmethod
@@ -6,6 +6,7 @@ This workflow is used to run tests on pull requests.
6
6
  from typing import Any
7
7
 
8
8
  from winipedia_utils.git.github.workflows.base.base import Workflow
9
+ from winipedia_utils.projects.poetry.config import PyprojectConfigFile
9
10
 
10
11
 
11
12
  class HealthCheckWorkflow(Workflow):
@@ -39,20 +40,41 @@ class HealthCheckWorkflow(Workflow):
39
40
  @classmethod
40
41
  def get_jobs(cls) -> dict[str, Any]:
41
42
  """Get the workflow jobs."""
43
+ matrix_job_name = f"{cls.get_filename()}_matrix"
42
44
  return {
43
45
  **cls.get_standard_job(
46
+ name=matrix_job_name,
47
+ runs_on="${{ matrix.os }}",
48
+ strategy={
49
+ "matrix": {
50
+ "os": ["ubuntu-latest", "windows-latest", "macos-latest"],
51
+ "python-version": [
52
+ str(v)
53
+ for v in PyprojectConfigFile.get_supported_python_versions()
54
+ ],
55
+ },
56
+ "fail-fast": True,
57
+ },
44
58
  steps=[
45
59
  *(
46
60
  cls.get_poetry_setup_steps(
47
61
  install_dependencies=True,
48
- token=True,
62
+ repo_token=True,
49
63
  with_keyring=True,
64
+ strategy_matrix=True,
50
65
  )
51
66
  ),
52
67
  cls.get_protect_repository_step(),
53
68
  cls.get_pre_commit_step(),
54
- cls.get_commit_step(),
55
- cls.get_extract_version_step(),
69
+ ],
70
+ ),
71
+ **cls.get_standard_job(
72
+ needs=[matrix_job_name],
73
+ steps=[
74
+ {
75
+ "name": "Aggregate Matrix Results",
76
+ "run": "echo 'Aggregating matrix results into one job.'",
77
+ }
56
78
  ],
57
79
  ),
58
80
  }
@@ -40,6 +40,12 @@ class ReleaseWorkflow(HealthCheckWorkflow):
40
40
  @classmethod
41
41
  def get_jobs(cls) -> dict[str, Any]:
42
42
  """Get the workflow jobs."""
43
- steps = super().get_jobs()
44
- steps[cls.get_filename()]["steps"].extend(cls.get_release_steps())
45
- return steps
43
+ jobs = HealthCheckWorkflow.get_jobs()
44
+ release_job = cls.get_standard_job(
45
+ needs=[HealthCheckWorkflow.get_filename()],
46
+ steps=[
47
+ *cls.get_release_steps(),
48
+ ],
49
+ )
50
+ jobs.update(release_job)
51
+ return jobs
@@ -38,7 +38,7 @@ class PreCommitConfigConfigFile(YamlConfigFile):
38
38
  {
39
39
  "id": hook_name,
40
40
  "name": hook_name,
41
- "entry": cls.get_python_setup_script(),
41
+ "entry": cls.get_poetry_run_setup_script(),
42
42
  "language": "system",
43
43
  "always_run": True,
44
44
  "pass_filenames": False,
@@ -7,15 +7,13 @@ strings are the arguments to the command. These funcs will be called by
7
7
  run_hooks.py, which will pass the returned list to subprocess.run().
8
8
  """
9
9
 
10
- from pathlib import Path
11
-
12
10
  from winipedia_utils.projects.poetry.poetry import (
13
11
  POETRY_ARG,
14
- get_run_python_module_args,
12
+ get_poetry_run_module_args,
15
13
  )
16
14
 
17
15
 
18
- def patch_version() -> list[str | Path]:
16
+ def patch_version() -> list[str]:
19
17
  """Patch the version in pyproject.toml.
20
18
 
21
19
  This function returns the input for subprocess.run() to patch the version
@@ -24,7 +22,7 @@ def patch_version() -> list[str | Path]:
24
22
  return [POETRY_ARG, "version", "patch"]
25
23
 
26
24
 
27
- def add_version_patch_to_git() -> list[str | Path]:
25
+ def add_version_patch_to_git() -> list[str]:
28
26
  """Add the version patch to git.
29
27
 
30
28
  This function returns the input for subprocess.run() to add the version
@@ -33,7 +31,7 @@ def add_version_patch_to_git() -> list[str | Path]:
33
31
  return ["git", "add", "pyproject.toml"]
34
32
 
35
33
 
36
- def update_package_manager() -> list[str | Path]:
34
+ def update_package_manager() -> list[str]:
37
35
  """Update the package manager.
38
36
 
39
37
  This function returns the input for subprocess.run() to update the package
@@ -42,7 +40,7 @@ def update_package_manager() -> list[str | Path]:
42
40
  return [POETRY_ARG, "self", "update"]
43
41
 
44
42
 
45
- def install_dependencies_with_dev() -> list[str | Path]:
43
+ def install_dependencies_with_dev() -> list[str]:
46
44
  """Install all dependencies.
47
45
 
48
46
  This function returns the input for subprocess.run() to install all dependencies.
@@ -50,7 +48,7 @@ def install_dependencies_with_dev() -> list[str | Path]:
50
48
  return [POETRY_ARG, "install", "--with", "dev"]
51
49
 
52
50
 
53
- def update_dependencies_with_dev() -> list[str | Path]:
51
+ def update_dependencies_with_dev() -> list[str]:
54
52
  """Update all dependencies.
55
53
 
56
54
  This function returns the input for subprocess.run() to update all dependencies.
@@ -58,7 +56,7 @@ def update_dependencies_with_dev() -> list[str | Path]:
58
56
  return [POETRY_ARG, "update", "--with", "dev"]
59
57
 
60
58
 
61
- def add_updates_to_git() -> list[str | Path]:
59
+ def add_updates_to_git() -> list[str]:
62
60
  """Add the updated dependencies to git.
63
61
 
64
62
  This function returns the input for subprocess.run() to add the updated
@@ -67,7 +65,7 @@ def add_updates_to_git() -> list[str | Path]:
67
65
  return ["git", "add", "pyproject.toml"]
68
66
 
69
67
 
70
- def lock_dependencies() -> list[str | Path]:
68
+ def lock_dependencies() -> list[str]:
71
69
  """Lock the dependencies.
72
70
 
73
71
  This function returns the input for subprocess.run() to lock the dependencies.
@@ -75,7 +73,7 @@ def lock_dependencies() -> list[str | Path]:
75
73
  return [POETRY_ARG, "lock"]
76
74
 
77
75
 
78
- def add_lock_file_to_git() -> list[str | Path]:
76
+ def add_lock_file_to_git() -> list[str]:
79
77
  """Add the lock file to git.
80
78
 
81
79
  This function returns the input for subprocess.run() to add the lock file
@@ -84,7 +82,7 @@ def add_lock_file_to_git() -> list[str | Path]:
84
82
  return ["git", "add", "poetry.lock"]
85
83
 
86
84
 
87
- def check_package_manager_configs() -> list[str | Path]:
85
+ def check_package_manager_configs() -> list[str]:
88
86
  """Check that poetry.lock and pyproject.toml is up to date.
89
87
 
90
88
  This function returns the input for subprocess.run() to check that poetry.lock
@@ -93,7 +91,7 @@ def check_package_manager_configs() -> list[str | Path]:
93
91
  return [POETRY_ARG, "check", "--strict"]
94
92
 
95
93
 
96
- def create_missing_tests() -> list[str | Path]:
94
+ def create_missing_tests() -> list[str]:
97
95
  """Create all tests for the project.
98
96
 
99
97
  This function returns the input for subprocess.run() to create all tests.
@@ -102,10 +100,10 @@ def create_missing_tests() -> list[str | Path]:
102
100
  create_tests,
103
101
  )
104
102
 
105
- return [*get_run_python_module_args(create_tests)]
103
+ return get_poetry_run_module_args(create_tests)
106
104
 
107
105
 
108
- def lint_code() -> list[str | Path]:
106
+ def lint_code() -> list[str]:
109
107
  """Check the code.
110
108
 
111
109
  This function returns the input for subprocess.run() to lint the code.
@@ -114,7 +112,7 @@ def lint_code() -> list[str | Path]:
114
112
  return ["ruff", "check", "--fix"]
115
113
 
116
114
 
117
- def format_code() -> list[str | Path]:
115
+ def format_code() -> list[str]:
118
116
  """Format the code.
119
117
 
120
118
  This function calls ruff format to format the code.
@@ -122,7 +120,7 @@ def format_code() -> list[str | Path]:
122
120
  return ["ruff", "format"]
123
121
 
124
122
 
125
- def check_static_types() -> list[str | Path]:
123
+ def check_static_types() -> list[str]:
126
124
  """Check the types.
127
125
 
128
126
  This function returns the input for subprocess.run() to check the static types.
@@ -130,7 +128,7 @@ def check_static_types() -> list[str | Path]:
130
128
  return ["mypy", "--exclude-gitignore"]
131
129
 
132
130
 
133
- def check_security() -> list[str | Path]:
131
+ def check_security() -> list[str]:
134
132
  """Check the security of the code.
135
133
 
136
134
  This function returns the input for subprocess.run() to check the security of
@@ -139,7 +137,7 @@ def check_security() -> list[str | Path]:
139
137
  return ["bandit", "-c", "pyproject.toml", "-r", "."]
140
138
 
141
139
 
142
- def run_tests() -> list[str | Path]:
140
+ def run_tests() -> list[str]:
143
141
  """Run the tests.
144
142
 
145
143
  This function returns the input for subprocess.run() to run all tests.
@@ -28,10 +28,22 @@ def run_hooks() -> None:
28
28
  passed = result.returncode == 0
29
29
 
30
30
  log_method = logger.info
31
- passed_str = (f"{GREEN}PASSED" if passed else f"{RED}FAILED") + RESET
31
+ status_str = (f"{GREEN}PASSED" if passed else f"{RED}FAILED") + RESET
32
32
  if not passed:
33
33
  log_method = logger.error
34
- passed_str += f"\n{result.stdout}"
34
+ status_str += f"""
35
+ ---------------------------------------------------------------------------------------------
36
+ Stdout:
37
+
38
+ {result.stdout}
39
+
40
+ ---------------------------------------------------------------------------------------------
41
+ Stderr:
42
+
43
+ {result.stderr}
44
+
45
+ ---------------------------------------------------------------------------------------------
46
+ """
35
47
  exit_code = 1
36
48
  # make the dashes always the same lentgth by adjusting to len of hook name
37
49
  num_dashes = 50 - len(hook_func.__name__)
@@ -39,7 +51,7 @@ def run_hooks() -> None:
39
51
  "Hook %s -%s> %s",
40
52
  hook_func.__name__,
41
53
  "-" * num_dashes,
42
- passed_str,
54
+ status_str,
43
55
  )
44
56
 
45
57
  if exit_code != 0:
@@ -13,10 +13,14 @@ from types import ModuleType
13
13
  from typing import Any
14
14
 
15
15
  from winipedia_utils.modules.function import is_func
16
+ from winipedia_utils.modules.inspection import get_def_line, get_obj_members
16
17
 
17
18
 
18
19
  def get_all_methods_from_cls(
19
- class_: type, *, exclude_parent_methods: bool = False
20
+ class_: type,
21
+ *,
22
+ exclude_parent_methods: bool = False,
23
+ include_annotate: bool = False,
20
24
  ) -> list[Callable[..., Any]]:
21
25
  """Get all methods from a class.
22
26
 
@@ -27,17 +31,21 @@ def get_all_methods_from_cls(
27
31
  class_: The class to extract methods from
28
32
  exclude_parent_methods: If True, only include methods defined in this class,
29
33
  excluding those inherited from parent classes
34
+ include_annotate: If False, exclude __annotate__ method
35
+ introduced in Python 3.14, defaults to False
36
+
30
37
  Returns:
31
38
  A list of callable methods from the class
32
39
 
33
40
  """
34
41
  from winipedia_utils.modules.module import ( # noqa: PLC0415 # avoid circular import
35
- get_def_line,
36
42
  get_module_of_obj,
37
43
  )
38
44
 
39
45
  methods = [
40
- (method, name) for name, method in inspect.getmembers(class_) if is_func(method)
46
+ (method, name)
47
+ for name, method in get_obj_members(class_, include_annotate=include_annotate)
48
+ if is_func(method)
41
49
  ]
42
50
 
43
51
  if exclude_parent_methods:
@@ -67,7 +75,6 @@ def get_all_cls_from_module(module: ModuleType | str) -> list[type]:
67
75
 
68
76
  """
69
77
  from winipedia_utils.modules.module import ( # noqa: PLC0415 # avoid circular import
70
- get_def_line,
71
78
  get_module_of_obj,
72
79
  )
73
80
 
@@ -12,6 +12,8 @@ from importlib import import_module
12
12
  from types import ModuleType
13
13
  from typing import Any
14
14
 
15
+ from winipedia_utils.modules.inspection import get_def_line, get_obj_members
16
+
15
17
 
16
18
  def is_func_or_method(obj: Any) -> bool:
17
19
  """Return True if *obj* is a function or method.
@@ -57,7 +59,9 @@ def is_func(obj: Any) -> bool:
57
59
  return is_func_or_method(unwrapped)
58
60
 
59
61
 
60
- def get_all_functions_from_module(module: ModuleType | str) -> list[Callable[..., Any]]:
62
+ def get_all_functions_from_module(
63
+ module: ModuleType | str, *, include_annotate: bool = False
64
+ ) -> list[Callable[..., Any]]:
61
65
  """Get all functions defined in a module.
62
66
 
63
67
  Retrieves all function objects that are defined directly in the specified module,
@@ -66,13 +70,14 @@ def get_all_functions_from_module(module: ModuleType | str) -> list[Callable[...
66
70
 
67
71
  Args:
68
72
  module: The module to extract functions from
73
+ include_annotate: If False, exclude __annotate__ method
74
+ introduced in Python 3.14, defaults to False
69
75
 
70
76
  Returns:
71
77
  A list of callable functions defined in the module
72
78
 
73
79
  """
74
80
  from winipedia_utils.modules.module import ( # noqa: PLC0415 # avoid circular import
75
- get_def_line,
76
81
  get_module_of_obj,
77
82
  )
78
83
 
@@ -80,7 +85,8 @@ def get_all_functions_from_module(module: ModuleType | str) -> list[Callable[...
80
85
  module = import_module(module)
81
86
  funcs = [
82
87
  func
83
- for _name, func in inspect.getmembers(module, is_func)
88
+ for _name, func in get_obj_members(module, include_annotate=include_annotate)
89
+ if is_func(func)
84
90
  if get_module_of_obj(func).__name__ == module.__name__
85
91
  ]
86
92
  # sort by definition order
@@ -0,0 +1,56 @@
1
+ """Inspection utilities for introspecting Python objects.
2
+
3
+ This module provides utility functions for inspecting Python objects,
4
+ including checking if an object is a function or method, and unwrapping
5
+ methods to their underlying functions.
6
+ """
7
+
8
+ import inspect
9
+ import sys
10
+ from collections.abc import Callable
11
+ from typing import Any, cast
12
+
13
+
14
+ def get_obj_members(
15
+ obj: Any, *, include_annotate: bool = False
16
+ ) -> list[tuple[str, Any]]:
17
+ """Get all members of an object."""
18
+ members = [(member, value) for member, value in inspect.getmembers(obj)]
19
+ if not include_annotate:
20
+ members = [
21
+ (member, value)
22
+ for member, value in members
23
+ if member not in ("__annotate__", "__annotate_func__")
24
+ ]
25
+ return members
26
+
27
+
28
+ def inside_frozen_bundle() -> bool:
29
+ """Return True if the code is running inside a frozen bundle."""
30
+ return getattr(sys, "frozen", False)
31
+
32
+
33
+ def get_def_line(obj: Any) -> int:
34
+ """Return the line number where a method-like object is defined."""
35
+ if isinstance(obj, property):
36
+ obj = obj.fget
37
+ unwrapped = inspect.unwrap(obj)
38
+ if hasattr(unwrapped, "__code__"):
39
+ return int(unwrapped.__code__.co_firstlineno)
40
+ # getsourcelines does not work if in a pyinstaller bundle or something
41
+ if inside_frozen_bundle():
42
+ return 0
43
+ return inspect.getsourcelines(unwrapped)[1]
44
+
45
+
46
+ def get_unwrapped_obj(obj: Any) -> Any:
47
+ """Return the unwrapped version of a method-like object."""
48
+ if isinstance(obj, property):
49
+ obj = obj.fget # get the getter function of the property
50
+ return inspect.unwrap(obj)
51
+
52
+
53
+ def get_qualname_of_obj(obj: Callable[..., Any] | type) -> str:
54
+ """Return the name of a method-like object."""
55
+ unwrapped = get_unwrapped_obj(obj)
56
+ return cast("str", unwrapped.__qualname__)
@@ -17,7 +17,7 @@ from collections.abc import Callable, Sequence
17
17
  from importlib import import_module
18
18
  from pathlib import Path
19
19
  from types import ModuleType
20
- from typing import Any, cast
20
+ from typing import Any
21
21
 
22
22
  from winipedia_utils.logging.logger import get_logger
23
23
  from winipedia_utils.modules.class_ import (
@@ -25,6 +25,7 @@ from winipedia_utils.modules.class_ import (
25
25
  get_all_methods_from_cls,
26
26
  )
27
27
  from winipedia_utils.modules.function import get_all_functions_from_module
28
+ from winipedia_utils.modules.inspection import get_qualname_of_obj, get_unwrapped_obj
28
29
  from winipedia_utils.modules.package import (
29
30
  get_modules_and_packages_from_package,
30
31
  make_dir_with_init_file,
@@ -329,24 +330,6 @@ def get_default_module_content() -> str:
329
330
  return '''"""module."""'''
330
331
 
331
332
 
332
- def inside_frozen_bundle() -> bool:
333
- """Return True if the code is running inside a frozen bundle."""
334
- return getattr(sys, "frozen", False)
335
-
336
-
337
- def get_def_line(obj: Any) -> int:
338
- """Return the line number where a method-like object is defined."""
339
- if isinstance(obj, property):
340
- obj = obj.fget
341
- unwrapped = inspect.unwrap(obj)
342
- if hasattr(unwrapped, "__code__"):
343
- return int(unwrapped.__code__.co_firstlineno)
344
- # getsourcelines does not work if in a pyinstaller bundle or something
345
- if inside_frozen_bundle():
346
- return 0
347
- return inspect.getsourcelines(unwrapped)[1]
348
-
349
-
350
333
  def get_module_of_obj(obj: Any, default: ModuleType | None = None) -> ModuleType:
351
334
  """Return the module name where a method-like object is defined.
352
335
 
@@ -368,19 +351,6 @@ def get_module_of_obj(obj: Any, default: ModuleType | None = None) -> ModuleType
368
351
  return module
369
352
 
370
353
 
371
- def get_qualname_of_obj(obj: Callable[..., Any] | type) -> str:
372
- """Return the name of a method-like object."""
373
- unwrapped = get_unwrapped_obj(obj)
374
- return cast("str", unwrapped.__qualname__)
375
-
376
-
377
- def get_unwrapped_obj(obj: Any) -> Any:
378
- """Return the unwrapped version of a method-like object."""
379
- if isinstance(obj, property):
380
- obj = obj.fget # get the getter function of the property
381
- return inspect.unwrap(obj)
382
-
383
-
384
354
  def get_executing_module() -> ModuleType:
385
355
  """Get the module where execution has started.
386
356