hunterMakesPy 0.2.4__py3-none-any.whl → 0.3.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.
- hunterMakesPy/__init__.py +17 -11
- hunterMakesPy/_theSSOT.py +1 -1
- hunterMakesPy/coping.py +2 -2
- hunterMakesPy/dataStructures.py +34 -24
- hunterMakesPy/filesystemToolkit.py +58 -3
- hunterMakesPy/parseParameters.py +14 -11
- hunterMakesPy/tests/test_filesystemToolkit.py +187 -1
- hunterMakesPy/tests/test_parseParameters.py +32 -30
- {huntermakespy-0.2.4.dist-info → huntermakespy-0.3.1.dist-info}/METADATA +5 -14
- huntermakespy-0.3.1.dist-info/RECORD +20 -0
- huntermakespy-0.2.4.dist-info/RECORD +0 -20
- {huntermakespy-0.2.4.dist-info → huntermakespy-0.3.1.dist-info}/WHEEL +0 -0
- {huntermakespy-0.2.4.dist-info → huntermakespy-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {huntermakespy-0.2.4.dist-info → huntermakespy-0.3.1.dist-info}/top_level.txt +0 -0
hunterMakesPy/__init__.py
CHANGED
|
@@ -7,23 +7,29 @@ This package provides:
|
|
|
7
7
|
- Utilities for string extraction from nested data structures and merging dictionaries of lists.
|
|
8
8
|
|
|
9
9
|
"""
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
# isort: split
|
|
11
12
|
from hunterMakesPy.theTypes import identifierDotAttribute as identifierDotAttribute
|
|
12
13
|
|
|
14
|
+
# isort: split
|
|
13
15
|
from hunterMakesPy.coping import PackageSettings as PackageSettings, raiseIfNone as raiseIfNone
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
# isort: split
|
|
18
|
+
from hunterMakesPy.parseParameters import (
|
|
19
|
+
defineConcurrencyLimit as defineConcurrencyLimit, intInnit as intInnit, oopsieKwargsie as oopsieKwargsie)
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
# isort: split
|
|
22
|
+
from hunterMakesPy.filesystemToolkit import (
|
|
23
|
+
importLogicalPath2Identifier as importLogicalPath2Identifier,
|
|
19
24
|
importPathFilename2Identifier as importPathFilename2Identifier, makeDirsSafely as makeDirsSafely,
|
|
20
|
-
writeStringToHere as writeStringToHere)
|
|
21
|
-
|
|
22
|
-
from hunterMakesPy.dataStructures import stringItUp as stringItUp, updateExtendPolishDictionaryLists as updateExtendPolishDictionaryLists
|
|
25
|
+
writePython as writePython, writeStringToHere as writeStringToHere)
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
# isort: split
|
|
28
|
+
from hunterMakesPy.dataStructures import (
|
|
29
|
+
stringItUp as stringItUp, updateExtendPolishDictionaryLists as updateExtendPolishDictionaryLists)
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
# isort: split
|
|
32
|
+
from hunterMakesPy.dataStructures import autoDecodingRLE as autoDecodingRLE
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
# isort: split
|
|
35
|
+
from hunterMakesPy._theSSOT import settingsPackage # pyright: ignore[reportUnusedImport]
|
hunterMakesPy/_theSSOT.py
CHANGED
hunterMakesPy/coping.py
CHANGED
|
@@ -22,7 +22,7 @@ def getPathPackageINSTALLING(identifierPackage: str) -> Path:
|
|
|
22
22
|
try:
|
|
23
23
|
moduleSpecification: ModuleSpec | None = find_spec(identifierPackage)
|
|
24
24
|
if moduleSpecification and moduleSpecification.origin:
|
|
25
|
-
pathFilename = Path(moduleSpecification.origin)
|
|
25
|
+
pathFilename: Path = Path(moduleSpecification.origin)
|
|
26
26
|
return pathFilename.parent if pathFilename.is_file() else pathFilename
|
|
27
27
|
except ModuleNotFoundError:
|
|
28
28
|
pass
|
|
@@ -148,6 +148,6 @@ def raiseIfNone(expression: TypeSansNone | None, errorMessage: str | None = None
|
|
|
148
148
|
|
|
149
149
|
"""
|
|
150
150
|
if expression is None:
|
|
151
|
-
message = errorMessage or 'A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`.'
|
|
151
|
+
message: str = errorMessage or 'A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`.'
|
|
152
152
|
raise ValueError(message)
|
|
153
153
|
return expression
|
hunterMakesPy/dataStructures.py
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"""Provides utilities for string extraction from nested data structures and merges multiple dictionaries containing lists into one dictionary."""
|
|
2
2
|
|
|
3
|
-
from collections.abc import
|
|
3
|
+
from collections.abc import Mapping
|
|
4
4
|
from numpy import integer
|
|
5
5
|
from numpy.typing import NDArray
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any, Protocol, TYPE_CHECKING, TypeVar
|
|
7
7
|
import more_itertools
|
|
8
8
|
import re as regex
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from collections.abc import Iterator
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
spaces after commas, around brackets and parentheses.
|
|
16
|
-
"""
|
|
13
|
+
def removeExtraWhitespace(text: str) -> str:
|
|
14
|
+
"""Remove extra whitespace from string representation of Python data structures."""
|
|
17
15
|
# Remove spaces after commas
|
|
18
16
|
text = regex.sub(r',\s+', ',', text)
|
|
19
17
|
# Remove spaces after opening brackets/parens
|
|
@@ -193,13 +191,13 @@ def stringItUp(*scrapPile: Any) -> list[str]:
|
|
|
193
191
|
case str():
|
|
194
192
|
listStrungUp.append(KitKat)
|
|
195
193
|
case bool() | bytearray() | bytes() | complex() | float() | int() | memoryview() | None:
|
|
196
|
-
listStrungUp.append(str(KitKat))
|
|
194
|
+
listStrungUp.append(str(KitKat))
|
|
197
195
|
case dict():
|
|
198
|
-
for broken, piece in KitKat.items():
|
|
196
|
+
for broken, piece in KitKat.items():
|
|
199
197
|
drill(broken)
|
|
200
198
|
drill(piece)
|
|
201
199
|
case list() | tuple() | set() | frozenset() | range():
|
|
202
|
-
for kit in KitKat:
|
|
200
|
+
for kit in KitKat:
|
|
203
201
|
drill(kit)
|
|
204
202
|
case _:
|
|
205
203
|
if hasattr(KitKat, '__iter__'): # Unpack other iterables
|
|
@@ -223,38 +221,50 @@ def stringItUp(*scrapPile: Any) -> list[str]:
|
|
|
223
221
|
listStrungUp.append(repr(scrap))
|
|
224
222
|
return listStrungUp
|
|
225
223
|
|
|
226
|
-
|
|
227
|
-
"""
|
|
224
|
+
class Ordinals(Protocol):
|
|
225
|
+
"""Protocol for types that support ordering comparisons."""
|
|
226
|
+
|
|
227
|
+
def __le__(self, other: "Ordinals", /) -> bool:
|
|
228
|
+
"""Less than or equal to comparison."""
|
|
229
|
+
...
|
|
230
|
+
def __ge__(self, other: "Ordinals", /) -> bool:
|
|
231
|
+
"""Greater than or equal to comparison."""
|
|
232
|
+
...
|
|
228
233
|
|
|
229
|
-
|
|
234
|
+
小于 = TypeVar('小于', bound=Ordinals)
|
|
235
|
+
|
|
236
|
+
def updateExtendPolishDictionaryLists(*dictionaryLists: Mapping[str, list[小于] | set[小于] | tuple[小于, ...]], destroyDuplicates: bool = False, reorderLists: bool = False, killErroneousDataTypes: bool = False) -> dict[str, list[小于]]:
|
|
237
|
+
"""Merge multiple dictionaries with `list` values into a single dictionary with the `list` values merged.
|
|
238
|
+
|
|
239
|
+
Plus options to destroy duplicates, sort `list` values, and handle erroneous data types.
|
|
230
240
|
|
|
231
241
|
Parameters
|
|
232
242
|
----------
|
|
233
243
|
*dictionaryLists : Mapping[str, list[Any] | set[Any] | tuple[Any, ...]]
|
|
234
|
-
|
|
244
|
+
Variable number of dictionaries to be merged. If only one dictionary is passed, it will be "polished".
|
|
235
245
|
destroyDuplicates : bool = False
|
|
236
|
-
|
|
246
|
+
If `True`, removes duplicate elements from the `list`. Defaults to `False`.
|
|
237
247
|
reorderLists : bool = False
|
|
238
|
-
|
|
248
|
+
If `True`, sorts each `list` value. Defaults to `False`. The elements must be comparable; otherwise, a `TypeError` will be raised.
|
|
239
249
|
killErroneousDataTypes : bool = False
|
|
240
|
-
|
|
250
|
+
If `True`, suppresses any `TypeError` `Exception` and omits the dictionary key or value that caused the `Exception`.
|
|
251
|
+
Defaults to `False`.
|
|
241
252
|
|
|
242
253
|
Returns
|
|
243
254
|
-------
|
|
244
255
|
ePluribusUnum : dict[str, list[Any]]
|
|
245
|
-
|
|
246
|
-
it will be cleaned up based on the options.
|
|
256
|
+
A single dictionary with merged and optionally "polished" `list` values.
|
|
247
257
|
|
|
248
258
|
Notes
|
|
249
259
|
-----
|
|
250
260
|
The returned value, `ePluribusUnum`, is a so-called primitive dictionary (`dict`). Furthermore, every dictionary key is a
|
|
251
|
-
so-called primitive string (cf
|
|
261
|
+
so-called primitive string (*cf.* `str()`) and every dictionary value is a so-called primitive `list` (`list`). If
|
|
252
262
|
`dictionaryLists` has other data types, the data types will not be preserved. That could have unexpected consequences.
|
|
253
263
|
Conversion from the original data type to a `list`, for example, may not preserve the order even if you want the order to be
|
|
254
264
|
preserved.
|
|
255
265
|
|
|
256
266
|
"""
|
|
257
|
-
ePluribusUnum: dict[str, list[
|
|
267
|
+
ePluribusUnum: dict[str, list[小于]] = {}
|
|
258
268
|
|
|
259
269
|
for dictionaryListTarget in dictionaryLists:
|
|
260
270
|
for keyName, keyValue in dictionaryListTarget.items():
|
|
@@ -272,7 +282,7 @@ def updateExtendPolishDictionaryLists(*dictionaryLists: Mapping[str, list[Any] |
|
|
|
272
282
|
for ImaStr, ImaList in ePluribusUnum.items():
|
|
273
283
|
ePluribusUnum[ImaStr] = list(dict.fromkeys(ImaList))
|
|
274
284
|
if reorderLists:
|
|
275
|
-
for ImaStr,
|
|
276
|
-
ePluribusUnum[ImaStr] = sorted(
|
|
285
|
+
for ImaStr, ImaRichComparisonSupporter in ePluribusUnum.items():
|
|
286
|
+
ePluribusUnum[ImaStr] = sorted(ImaRichComparisonSupporter)
|
|
277
287
|
|
|
278
288
|
return ePluribusUnum
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
This module provides basic file I/O utilities such as importing callables from modules, safely creating directories, and writing to files or streams (pipes).
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
|
-
|
|
6
|
+
from autoflake import fix_code as autoflake_fix_code
|
|
7
7
|
from hunterMakesPy import identifierDotAttribute
|
|
8
|
+
from isort import code as isort_code
|
|
8
9
|
from os import PathLike
|
|
9
10
|
from pathlib import Path, PurePath
|
|
10
11
|
from typing import Any, TYPE_CHECKING, TypeVar
|
|
@@ -14,6 +15,7 @@ import importlib.util
|
|
|
14
15
|
import io
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
18
|
+
from importlib.machinery import ModuleSpec
|
|
17
19
|
from types import ModuleType
|
|
18
20
|
|
|
19
21
|
归个 = TypeVar('归个')
|
|
@@ -71,9 +73,9 @@ def importPathFilename2Identifier(pathFilename: PathLike[Any] | PurePath, identi
|
|
|
71
73
|
"""
|
|
72
74
|
pathFilename = Path(pathFilename)
|
|
73
75
|
|
|
74
|
-
importlibSpecification = importlib.util.spec_from_file_location(moduleIdentifier or pathFilename.stem, pathFilename)
|
|
76
|
+
importlibSpecification: ModuleSpec | None = importlib.util.spec_from_file_location(moduleIdentifier or pathFilename.stem, pathFilename)
|
|
75
77
|
if importlibSpecification is None or importlibSpecification.loader is None:
|
|
76
|
-
message = f"I received\n\t`{pathFilename = }`,\n\t`{identifier = }`, and\n\t`{moduleIdentifier = }`.\n\tAfter loading, \n\t`importlibSpecification` {'is `None`' if importlibSpecification is None else 'has a value'} and\n\t`importlibSpecification.loader` is unknown."
|
|
78
|
+
message: str = f"I received\n\t`{pathFilename = }`,\n\t`{identifier = }`, and\n\t`{moduleIdentifier = }`.\n\tAfter loading, \n\t`importlibSpecification` {'is `None`' if importlibSpecification is None else 'has a value'} and\n\t`importlibSpecification.loader` is unknown."
|
|
77
79
|
raise ImportError(message)
|
|
78
80
|
|
|
79
81
|
moduleImported_jk_hahaha: ModuleType = importlib.util.module_from_spec(importlibSpecification)
|
|
@@ -97,6 +99,59 @@ def makeDirsSafely(pathFilename: Any) -> None:
|
|
|
97
99
|
with contextlib.suppress(OSError):
|
|
98
100
|
Path(pathFilename).parent.mkdir(parents=True, exist_ok=True)
|
|
99
101
|
|
|
102
|
+
settings_autoflakeDEFAULT: dict[str, list[str] | bool] = {
|
|
103
|
+
'additional_imports': [],
|
|
104
|
+
'expand_star_imports': True,
|
|
105
|
+
'remove_all_unused_imports': True,
|
|
106
|
+
'remove_duplicate_keys': False,
|
|
107
|
+
'remove_unused_variables': False,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
settings_isortDEFAULT: dict[str, bool | int | str | list[str]] = {
|
|
111
|
+
"combine_as_imports": True,
|
|
112
|
+
"force_alphabetical_sort_within_sections": True,
|
|
113
|
+
"from_first": True,
|
|
114
|
+
"honor_noqa": True,
|
|
115
|
+
"indent": "\t",
|
|
116
|
+
"line_length": 120,
|
|
117
|
+
"lines_after_imports": 1,
|
|
118
|
+
"lines_between_types": 0,
|
|
119
|
+
"multi_line_output": 4,
|
|
120
|
+
"no_sections": True,
|
|
121
|
+
"skip": ["__init__.py"], # TODO think
|
|
122
|
+
"use_parentheses": True,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
def writePython(pythonSource: str, pathFilename: PathLike[Any] | PurePath | io.TextIOBase, settings: dict[str, dict[str, Any]] | None = None) -> None:
|
|
126
|
+
"""Format and write Python source code to a file or text stream.
|
|
127
|
+
|
|
128
|
+
(AI generated docstring)
|
|
129
|
+
|
|
130
|
+
This function processes Python source code through autoflake and isort formatters before writing to the specified destination.
|
|
131
|
+
The formatters remove unused imports, sort imports, and apply consistent code style according to the provided or default
|
|
132
|
+
settings.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
pythonSource : str
|
|
137
|
+
The Python source code to format and write.
|
|
138
|
+
pathFilename : PathLike[Any] | PurePath | io.TextIOBase
|
|
139
|
+
The target destination: either a file path or an open text stream.
|
|
140
|
+
settings : dict[str, dict[str, Any]] | None = None
|
|
141
|
+
Configuration for the formatters. Keys are `'autoflake'` and `'isort'`, each mapping to a dictionary of formatter-specific
|
|
142
|
+
settings. If `None`, default settings are used for both formatters.
|
|
143
|
+
|
|
144
|
+
"""
|
|
145
|
+
if settings is None:
|
|
146
|
+
settings = {}
|
|
147
|
+
|
|
148
|
+
settings_autoflake: dict[str, Any] = settings.get('autoflake', settings_autoflakeDEFAULT)
|
|
149
|
+
pythonSource = autoflake_fix_code(pythonSource, **settings_autoflake)
|
|
150
|
+
|
|
151
|
+
settings_isort: dict[str, Any] = settings.get('isort', settings_isortDEFAULT)
|
|
152
|
+
pythonSource = isort_code(pythonSource, **settings_isort)
|
|
153
|
+
writeStringToHere(pythonSource + '\n', pathFilename)
|
|
154
|
+
|
|
100
155
|
def writeStringToHere(this: str, pathFilename: PathLike[Any] | PurePath | io.TextIOBase) -> None:
|
|
101
156
|
"""Write a string to a file or text stream.
|
|
102
157
|
|
hunterMakesPy/parseParameters.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Provides parameter and input validation, integer parsing, and concurrency handling utilities."""
|
|
2
2
|
from collections.abc import Iterable, Sized
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, TYPE_CHECKING
|
|
5
5
|
import charset_normalizer
|
|
6
6
|
import multiprocessing
|
|
7
7
|
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from charset_normalizer.models import CharsetMatch
|
|
10
|
+
|
|
8
11
|
@dataclass
|
|
9
12
|
class ErrorMessageContext:
|
|
10
13
|
"""Context information for constructing error messages.
|
|
@@ -52,7 +55,7 @@ def _constructErrorMessage(context: ErrorMessageContext, parameterName: str, par
|
|
|
52
55
|
The constructed error message string.
|
|
53
56
|
|
|
54
57
|
"""
|
|
55
|
-
messageParts = ["I received "]
|
|
58
|
+
messageParts: list[str] = ["I received "]
|
|
56
59
|
|
|
57
60
|
if context.parameterValue is not None and not isinstance(context.parameterValue, (bytes, bytearray, memoryview)):
|
|
58
61
|
messageParts.append(f'"{context.parameterValue}"')
|
|
@@ -131,15 +134,15 @@ def defineConcurrencyLimit(*, limit: bool | float | int | None, cpuTotal: int =
|
|
|
131
134
|
```
|
|
132
135
|
|
|
133
136
|
"""
|
|
134
|
-
concurrencyLimit = cpuTotal
|
|
137
|
+
concurrencyLimit: int = cpuTotal
|
|
135
138
|
|
|
136
139
|
if isinstance(limit, str):
|
|
137
|
-
limitFromString = oopsieKwargsie(limit)
|
|
140
|
+
limitFromString: bool | None | str = oopsieKwargsie(limit)
|
|
138
141
|
if isinstance(limitFromString, str):
|
|
139
142
|
try:
|
|
140
143
|
limit = float(limitFromString)
|
|
141
144
|
except ValueError as ERRORmessage:
|
|
142
|
-
message = f"I received '{limitFromString}', but it must be a number, `True`, `False`, or `None`."
|
|
145
|
+
message: str = f"I received '{limitFromString}', but it must be a number, `True`, `False`, or `None`."
|
|
143
146
|
raise ValueError(message) from ERRORmessage
|
|
144
147
|
else:
|
|
145
148
|
limit = limitFromString
|
|
@@ -208,19 +211,19 @@ def intInnit(listInt_Allegedly: Iterable[Any], parameterName: str | None = None,
|
|
|
208
211
|
message = f"I did not receive a value for {parameterName}, but it is required."
|
|
209
212
|
raise ValueError(message)
|
|
210
213
|
|
|
211
|
-
# Be nice
|
|
214
|
+
# Be nice, and assume the input container is valid and every element is valid.
|
|
212
215
|
# Nevertheless, this is a "fail-early" step, so reject ambiguity and try to induce errors now that could be catastrophic later.
|
|
213
216
|
try:
|
|
214
217
|
iter(listInt_Allegedly)
|
|
215
|
-
lengthInitial = None
|
|
218
|
+
lengthInitial: int | None = None
|
|
216
219
|
if isinstance(listInt_Allegedly, Sized):
|
|
217
220
|
lengthInitial = len(listInt_Allegedly)
|
|
218
221
|
|
|
219
222
|
listValidated: list[int] = []
|
|
220
223
|
|
|
221
224
|
for allegedInt in listInt_Allegedly:
|
|
222
|
-
|
|
223
|
-
errorMessageContext = ErrorMessageContext(
|
|
225
|
+
|
|
226
|
+
errorMessageContext: ErrorMessageContext = ErrorMessageContext(
|
|
224
227
|
parameterValue = allegedInt,
|
|
225
228
|
parameterValueType = type(allegedInt).__name__,
|
|
226
229
|
isElement = True
|
|
@@ -237,7 +240,7 @@ def intInnit(listInt_Allegedly: Iterable[Any], parameterName: str | None = None,
|
|
|
237
240
|
errorMessageContext.parameterValue = None # Don't expose potentially garbled binary data in error messages
|
|
238
241
|
if isinstance(allegedInt, memoryview):
|
|
239
242
|
allegedInt = allegedInt.tobytes()
|
|
240
|
-
decodedString = charset_normalizer.from_bytes(allegedInt).best()
|
|
243
|
+
decodedString: CharsetMatch | None = charset_normalizer.from_bytes(allegedInt).best()
|
|
241
244
|
if not decodedString:
|
|
242
245
|
raise ValueError(errorMessageContext)
|
|
243
246
|
allegedInt = errorMessageContext.parameterValue = str(decodedString)
|
|
@@ -308,7 +311,7 @@ def oopsieKwargsie(huh: Any) -> bool | None | str:
|
|
|
308
311
|
huh = str(huh)
|
|
309
312
|
except BaseException: # noqa: BLE001
|
|
310
313
|
return huh
|
|
311
|
-
formatted = huh.strip().title()
|
|
314
|
+
formatted: str = huh.strip().title()
|
|
312
315
|
if formatted == str(True):
|
|
313
316
|
return True
|
|
314
317
|
if formatted == str(False):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# pyright: standard
|
|
2
|
-
from hunterMakesPy import
|
|
2
|
+
from hunterMakesPy import (
|
|
3
|
+
importLogicalPath2Identifier, importPathFilename2Identifier, makeDirsSafely, writePython, writeStringToHere)
|
|
3
4
|
from hunterMakesPy.tests.conftest import standardizedEqualTo
|
|
4
5
|
import io
|
|
5
6
|
import math
|
|
@@ -261,3 +262,188 @@ def testImportPathFilename2IdentifierWithValidFileInvalidIdentifier(
|
|
|
261
262
|
identifierTarget
|
|
262
263
|
)
|
|
263
264
|
|
|
265
|
+
|
|
266
|
+
@pytest.mark.parametrize(
|
|
267
|
+
"pythonSourceTarget, expectedFormattedContent",
|
|
268
|
+
[
|
|
269
|
+
(
|
|
270
|
+
"import sys\nimport os\nimport math\n\ndef fibonacciFunction():\n return 13\n",
|
|
271
|
+
"\ndef fibonacciFunction():\n return 13\n\n"
|
|
272
|
+
),
|
|
273
|
+
(
|
|
274
|
+
"from pathlib import Path\nimport sys\nimport os\n\ndef primeFunction():\n return 17\n",
|
|
275
|
+
"\ndef primeFunction():\n return 17\n\n"
|
|
276
|
+
),
|
|
277
|
+
(
|
|
278
|
+
"import unused\nimport sys\n\nvalueCardinal = sys.version\n",
|
|
279
|
+
"import sys\n\nvalueCardinal = sys.version\n\n"
|
|
280
|
+
),
|
|
281
|
+
(
|
|
282
|
+
"import math\n\nvalueFibonacci = math.sqrt(21)\n",
|
|
283
|
+
"import math\n\nvalueFibonacci = math.sqrt(21)\n\n"
|
|
284
|
+
),
|
|
285
|
+
]
|
|
286
|
+
)
|
|
287
|
+
def testWritePythonFormatsAndWritesToFile(
|
|
288
|
+
pathTmpTesting: pathlib.Path,
|
|
289
|
+
pythonSourceTarget: str,
|
|
290
|
+
expectedFormattedContent: str
|
|
291
|
+
) -> None:
|
|
292
|
+
"""Test that writePython formats Python source code and writes it to files."""
|
|
293
|
+
pathFilenameTarget = pathTmpTesting / "formattedModule.py"
|
|
294
|
+
writePython(pythonSourceTarget, pathFilenameTarget)
|
|
295
|
+
|
|
296
|
+
assert pathFilenameTarget.exists(), (
|
|
297
|
+
f"\nTesting: `writePython(..., {pathFilenameTarget})`\n"
|
|
298
|
+
f"Expected: File {pathFilenameTarget} to exist\n"
|
|
299
|
+
f"Got: exists={pathFilenameTarget.exists()}"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
contentActual = pathFilenameTarget.read_text(encoding="utf-8")
|
|
303
|
+
assert contentActual == expectedFormattedContent, (
|
|
304
|
+
f"\nTesting: `writePython(...)`\n"
|
|
305
|
+
f"Expected content:\n{repr(expectedFormattedContent)}\n"
|
|
306
|
+
f"Got content:\n{repr(contentActual)}"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@pytest.mark.parametrize(
|
|
311
|
+
"pythonSourceTarget, expectedFormattedContent",
|
|
312
|
+
[
|
|
313
|
+
(
|
|
314
|
+
"import sys\nimport unused\n\ndef cardinalFunction():\n return sys.version\n",
|
|
315
|
+
"import sys\n\ndef cardinalFunction():\n return sys.version\n\n"
|
|
316
|
+
),
|
|
317
|
+
(
|
|
318
|
+
"from pathlib import Path\nfrom os import getcwd\nimport math\n\nvalueSequence = getcwd()\n",
|
|
319
|
+
"from os import getcwd\n\nvalueSequence = getcwd()\n\n"
|
|
320
|
+
),
|
|
321
|
+
]
|
|
322
|
+
)
|
|
323
|
+
def testWritePythonFormatsAndWritesToStream(
|
|
324
|
+
pythonSourceTarget: str,
|
|
325
|
+
expectedFormattedContent: str
|
|
326
|
+
) -> None:
|
|
327
|
+
"""Test that writePython formats Python source code and writes it to IO streams."""
|
|
328
|
+
streamMemory = io.StringIO()
|
|
329
|
+
writePython(pythonSourceTarget, streamMemory)
|
|
330
|
+
|
|
331
|
+
contentActual = streamMemory.getvalue()
|
|
332
|
+
assert contentActual == expectedFormattedContent, (
|
|
333
|
+
f"\nTesting: `writePython(..., StringIO)`\n"
|
|
334
|
+
f"Expected content:\n{repr(expectedFormattedContent)}\n"
|
|
335
|
+
f"Got content:\n{repr(contentActual)}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@pytest.mark.parametrize(
|
|
340
|
+
"pythonSourceTarget, settingsCustom, expectedFormattedContent",
|
|
341
|
+
[
|
|
342
|
+
(
|
|
343
|
+
"import math\nimport unused\n\nvalueFibonacci = 34\n\ndef fibonacciFunction():\n return math.sqrt(13)\n",
|
|
344
|
+
{'autoflake': {'remove_all_unused_imports': True, 'remove_unused_variables': False}},
|
|
345
|
+
"import math\n\nvalueFibonacci = 34\n\ndef fibonacciFunction():\n return math.sqrt(13)\n\n"
|
|
346
|
+
),
|
|
347
|
+
(
|
|
348
|
+
"from pathlib import Path\nimport sys\n\nvaluePrime = Path.cwd()\n",
|
|
349
|
+
{'isort': {'force_alphabetical_sort_within_sections': False, 'from_first': False}},
|
|
350
|
+
"from pathlib import Path\n\nvaluePrime = Path.cwd()\n\n"
|
|
351
|
+
),
|
|
352
|
+
]
|
|
353
|
+
)
|
|
354
|
+
def testWritePythonWithCustomSettings(
|
|
355
|
+
pathTmpTesting: pathlib.Path,
|
|
356
|
+
pythonSourceTarget: str,
|
|
357
|
+
settingsCustom: dict[str, dict[str, object]],
|
|
358
|
+
expectedFormattedContent: str
|
|
359
|
+
) -> None:
|
|
360
|
+
"""Test that writePython respects custom formatter settings."""
|
|
361
|
+
pathFilenameTarget = pathTmpTesting / "customFormattedModule.py"
|
|
362
|
+
writePython(pythonSourceTarget, pathFilenameTarget, settingsCustom)
|
|
363
|
+
|
|
364
|
+
contentActual = pathFilenameTarget.read_text(encoding="utf-8")
|
|
365
|
+
assert contentActual == expectedFormattedContent, (
|
|
366
|
+
f"\nTesting: `writePython(..., custom settings)`\n"
|
|
367
|
+
f"Expected content:\n{repr(expectedFormattedContent)}\n"
|
|
368
|
+
f"Got content:\n{repr(contentActual)}"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@pytest.mark.parametrize(
|
|
373
|
+
"pythonSourceTarget",
|
|
374
|
+
[
|
|
375
|
+
"import math\nimport sys\n\nvalueFibonacci = 34\n",
|
|
376
|
+
"from pathlib import Path\n\nvalueCardinal = 'SW'\n",
|
|
377
|
+
"def primeFunction():\n return 37\n",
|
|
378
|
+
]
|
|
379
|
+
)
|
|
380
|
+
def testWritePythonCreatesNestedDirectories(
|
|
381
|
+
pathTmpTesting: pathlib.Path,
|
|
382
|
+
pythonSourceTarget: str
|
|
383
|
+
) -> None:
|
|
384
|
+
"""Test that writePython creates nested directories when writing to files."""
|
|
385
|
+
pathFilenameTarget = pathTmpTesting / "nested" / "directories" / "module.py"
|
|
386
|
+
writePython(pythonSourceTarget, pathFilenameTarget)
|
|
387
|
+
|
|
388
|
+
assert pathFilenameTarget.exists(), (
|
|
389
|
+
f"\nTesting: `writePython(..., {pathFilenameTarget})`\n"
|
|
390
|
+
f"Expected: File {pathFilenameTarget} to exist\n"
|
|
391
|
+
f"Got: exists={pathFilenameTarget.exists()}"
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
assert pathFilenameTarget.parent.exists(), (
|
|
395
|
+
f"\nTesting: `writePython(..., {pathFilenameTarget})`\n"
|
|
396
|
+
f"Expected: Parent directory {pathFilenameTarget.parent} to exist\n"
|
|
397
|
+
f"Got: exists={pathFilenameTarget.parent.exists()}"
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@pytest.mark.parametrize(
|
|
402
|
+
"pythonSourceTarget, expectedContainsImport",
|
|
403
|
+
[
|
|
404
|
+
("import math\n\nvaluePrime = math.sqrt(41)\n", "import math"),
|
|
405
|
+
("from os import getcwd\n\nvalueSequence = getcwd()\n", "from os import getcwd"),
|
|
406
|
+
("import sys\n\nvalueFibonacci = sys.version\n", "import sys"),
|
|
407
|
+
]
|
|
408
|
+
)
|
|
409
|
+
def testWritePythonPreservesUsedImports(
|
|
410
|
+
pathTmpTesting: pathlib.Path,
|
|
411
|
+
pythonSourceTarget: str,
|
|
412
|
+
expectedContainsImport: str
|
|
413
|
+
) -> None:
|
|
414
|
+
"""Test that writePython preserves imports that are actually used in the code."""
|
|
415
|
+
pathFilenameTarget = pathTmpTesting / "preservedImports.py"
|
|
416
|
+
writePython(pythonSourceTarget, pathFilenameTarget)
|
|
417
|
+
|
|
418
|
+
contentActual = pathFilenameTarget.read_text(encoding="utf-8")
|
|
419
|
+
assert expectedContainsImport in contentActual, (
|
|
420
|
+
f"\nTesting: `writePython(...)` preserves used imports\n"
|
|
421
|
+
f"Expected content to contain: {expectedContainsImport}\n"
|
|
422
|
+
f"Got content:\n{contentActual}"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@pytest.mark.parametrize(
|
|
427
|
+
"pythonSourceTarget, expectedNotContainsImport",
|
|
428
|
+
[
|
|
429
|
+
("import math\nimport unused\n\nvaluePrime = 43\n", "import unused"),
|
|
430
|
+
("from os import getcwd, unused\n\nvalueSequence = getcwd()\n", "unused"),
|
|
431
|
+
("import sys\nimport collections\n\nvalueFibonacci = sys.version\n", "import collections"),
|
|
432
|
+
]
|
|
433
|
+
)
|
|
434
|
+
def testWritePythonRemovesUnusedImports(
|
|
435
|
+
pathTmpTesting: pathlib.Path,
|
|
436
|
+
pythonSourceTarget: str,
|
|
437
|
+
expectedNotContainsImport: str
|
|
438
|
+
) -> None:
|
|
439
|
+
"""Test that writePython removes imports that are not used in the code."""
|
|
440
|
+
pathFilenameTarget = pathTmpTesting / "removedImports.py"
|
|
441
|
+
writePython(pythonSourceTarget, pathFilenameTarget)
|
|
442
|
+
|
|
443
|
+
contentActual = pathFilenameTarget.read_text(encoding="utf-8")
|
|
444
|
+
assert expectedNotContainsImport not in contentActual, (
|
|
445
|
+
f"\nTesting: `writePython(...)` removes unused imports\n"
|
|
446
|
+
f"Expected content to NOT contain: {expectedNotContainsImport}\n"
|
|
447
|
+
f"Got content:\n{contentActual}"
|
|
448
|
+
)
|
|
449
|
+
|
|
@@ -8,7 +8,7 @@ import pytest
|
|
|
8
8
|
parameters = ParamSpec('parameters')
|
|
9
9
|
returnType = TypeVar('returnType')
|
|
10
10
|
|
|
11
|
-
def PytestFor_defineConcurrencyLimit(callableToTest: Callable[..., int] = defineConcurrencyLimit, cpuCount: int = 8) -> list[tuple[str, Callable[[], None]]]:
|
|
11
|
+
def PytestFor_defineConcurrencyLimit(callableToTest: Callable[..., int] = defineConcurrencyLimit, cpuCount: int = 8) -> list[tuple[str, Callable[[], None]]]:
|
|
12
12
|
"""Return a list of test functions to validate concurrency limit behavior.
|
|
13
13
|
|
|
14
14
|
This function provides a comprehensive test suite for validating concurrency limit parsing
|
|
@@ -80,15 +80,17 @@ def PytestFor_defineConcurrencyLimit(callableToTest: Callable[..., int] = define
|
|
|
80
80
|
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
81
81
|
def testBooleanTrue(_mockCpu: Mock) -> None:
|
|
82
82
|
assert callableToTest(limit=True, cpuTotal=cpuCount) == 1
|
|
83
|
-
|
|
84
|
-
assert callableToTest(limit='
|
|
85
|
-
assert callableToTest(limit='
|
|
83
|
+
# pyright: reportArgumentType=false
|
|
84
|
+
assert callableToTest(limit='True', cpuTotal=cpuCount) == 1
|
|
85
|
+
assert callableToTest(limit='TRUE', cpuTotal=cpuCount) == 1
|
|
86
|
+
assert callableToTest(limit=' true ', cpuTotal=cpuCount) == 1
|
|
87
|
+
# pyright: reportArgumentType=true
|
|
86
88
|
|
|
87
89
|
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
88
90
|
def testInvalidStrings(_mockCpu: Mock) -> None:
|
|
89
91
|
for stringInput in ["invalid", "True but not quite", "None of the above"]:
|
|
90
92
|
with pytest.raises(ValueError, match="must be a number, `True`, `False`, or `None`"):
|
|
91
|
-
callableToTest(limit=stringInput, cpuTotal=cpuCount)
|
|
93
|
+
callableToTest(limit=stringInput, cpuTotal=cpuCount)
|
|
92
94
|
|
|
93
95
|
@patch('multiprocessing.cpu_count', return_value=cpuCount)
|
|
94
96
|
def testStringNumbers(_mockCpu: Mock) -> None:
|
|
@@ -100,7 +102,7 @@ def PytestFor_defineConcurrencyLimit(callableToTest: Callable[..., int] = define
|
|
|
100
102
|
("-0.25", 6),
|
|
101
103
|
]
|
|
102
104
|
for stringNumber, expectedLimit in testCases:
|
|
103
|
-
assert callableToTest(limit=stringNumber, cpuTotal=cpuCount) == expectedLimit
|
|
105
|
+
assert callableToTest(limit=stringNumber, cpuTotal=cpuCount) == expectedLimit
|
|
104
106
|
|
|
105
107
|
return [
|
|
106
108
|
('testDefaults', testDefaults),
|
|
@@ -112,7 +114,7 @@ def PytestFor_defineConcurrencyLimit(callableToTest: Callable[..., int] = define
|
|
|
112
114
|
('testStringNumbers', testStringNumbers)
|
|
113
115
|
]
|
|
114
116
|
|
|
115
|
-
def PytestFor_intInnit(callableToTest: Callable[[Iterable[int], str | None, type[Any] | None], list[int]] = intInnit) -> list[tuple[str, Callable[[], None]]]:
|
|
117
|
+
def PytestFor_intInnit(callableToTest: Callable[[Iterable[int], str | None, type[Any] | None], list[int]] = intInnit) -> list[tuple[str, Callable[[], None]]]:
|
|
116
118
|
"""Return a list of test functions to validate integer initialization behavior.
|
|
117
119
|
|
|
118
120
|
This function provides a comprehensive test suite for validating integer parsing
|
|
@@ -156,64 +158,64 @@ def PytestFor_intInnit(callableToTest: Callable[[Iterable[int], str | None, type
|
|
|
156
158
|
"""
|
|
157
159
|
def testHandlesValidIntegers() -> None:
|
|
158
160
|
assert callableToTest([2, 3, 5, 8], 'test', None) == [2, 3, 5, 8]
|
|
159
|
-
assert callableToTest([13.0, 21.0, 34.0], 'test', None) == [13, 21, 34]
|
|
160
|
-
assert callableToTest(['55', '89', '144'], 'test', None) == [55, 89, 144]
|
|
161
|
-
assert callableToTest([' 233 ', '377', '-610'], 'test', None) == [233, 377, -610]
|
|
161
|
+
assert callableToTest([13.0, 21.0, 34.0], 'test', None) == [13, 21, 34]
|
|
162
|
+
assert callableToTest(['55', '89', '144'], 'test', None) == [55, 89, 144]
|
|
163
|
+
assert callableToTest([' 233 ', '377', '-610'], 'test', None) == [233, 377, -610]
|
|
162
164
|
|
|
163
165
|
def testRejectsNonWholeNumbers() -> None:
|
|
164
166
|
listInvalidNumbers: list[float] = [13.7, 21.5, 34.8, -55.9]
|
|
165
167
|
for invalidNumber in listInvalidNumbers:
|
|
166
168
|
with pytest.raises(ValueError):
|
|
167
|
-
callableToTest([invalidNumber], 'test', None)
|
|
168
|
-
|
|
169
|
-
def testRejectsBooleans() -> None:
|
|
170
|
-
with pytest.raises(TypeError):
|
|
171
|
-
callableToTest([True, False], 'test', None)
|
|
169
|
+
callableToTest([invalidNumber], 'test', None)
|
|
172
170
|
|
|
173
171
|
def testRejectsInvalidStrings() -> None:
|
|
174
172
|
for invalidString in ['NW', '', ' ', 'SE.SW']:
|
|
175
173
|
with pytest.raises(ValueError):
|
|
176
|
-
callableToTest([invalidString], 'test', None)
|
|
174
|
+
callableToTest([invalidString], 'test', None)
|
|
175
|
+
|
|
176
|
+
def testHandlesMixedValidTypes() -> None:
|
|
177
|
+
assert callableToTest([13, '21', 34.0], 'test', None) == [13, 21, 34]
|
|
178
|
+
|
|
179
|
+
def testRejectsBooleans() -> None:
|
|
180
|
+
with pytest.raises(TypeError):
|
|
181
|
+
callableToTest([True, False], 'test', None)
|
|
177
182
|
|
|
178
183
|
def testRejectsEmptyList() -> None:
|
|
179
184
|
with pytest.raises(ValueError):
|
|
180
185
|
callableToTest([], 'test', None)
|
|
181
186
|
|
|
182
|
-
def testHandlesMixedValidTypes() -> None:
|
|
183
|
-
assert callableToTest([13, '21', 34.0], 'test', None) == [13, 21, 34] # pyright: ignore[reportArgumentType]
|
|
184
|
-
|
|
185
187
|
def testHandlesBytes() -> None:
|
|
186
188
|
validCases: list[tuple[list[bytes], str, list[int]]] = [
|
|
187
189
|
([b'123'], '123', [123]),
|
|
188
190
|
]
|
|
189
191
|
for inputData, testName, expected in validCases:
|
|
190
|
-
assert callableToTest(inputData, testName, None) == expected
|
|
192
|
+
assert callableToTest(inputData, testName, None) == expected
|
|
191
193
|
|
|
192
194
|
extendedCases: list[tuple[list[bytes], str, list[int]]] = [
|
|
193
195
|
([b'123456789'], '123456789', [123456789]),
|
|
194
196
|
]
|
|
195
197
|
for inputData, testName, expected in extendedCases:
|
|
196
|
-
assert callableToTest(inputData, testName, None) == expected
|
|
198
|
+
assert callableToTest(inputData, testName, None) == expected
|
|
197
199
|
|
|
198
200
|
invalidCases: list[list[bytes]] = [[b'\x00']]
|
|
199
201
|
for inputData in invalidCases:
|
|
200
202
|
with pytest.raises(ValueError):
|
|
201
|
-
callableToTest(inputData, 'test', None)
|
|
203
|
+
callableToTest(inputData, 'test', None)
|
|
202
204
|
|
|
203
205
|
def testHandlesMemoryview() -> None:
|
|
204
206
|
validCases: list[tuple[list[memoryview], str, list[int]]] = [
|
|
205
207
|
([memoryview(b'123')], '123', [123]),
|
|
206
208
|
]
|
|
207
209
|
for inputData, testName, expected in validCases:
|
|
208
|
-
assert callableToTest(inputData, testName, None) == expected
|
|
210
|
+
assert callableToTest(inputData, testName, None) == expected
|
|
209
211
|
|
|
210
212
|
largeMemoryviewCase: list[memoryview] = [memoryview(b'9999999999')]
|
|
211
|
-
assert callableToTest(largeMemoryviewCase, 'test', None) == [9999999999]
|
|
213
|
+
assert callableToTest(largeMemoryviewCase, 'test', None) == [9999999999]
|
|
212
214
|
|
|
213
215
|
invalidMemoryviewCases: list[list[memoryview]] = [[memoryview(b'\x00')]]
|
|
214
216
|
for inputData in invalidMemoryviewCases:
|
|
215
217
|
with pytest.raises(ValueError):
|
|
216
|
-
callableToTest(inputData, 'test', None)
|
|
218
|
+
callableToTest(inputData, 'test', None)
|
|
217
219
|
|
|
218
220
|
def testRejectsMutableSequence() -> None:
|
|
219
221
|
class MutableList(list[int]):
|
|
@@ -229,12 +231,12 @@ def PytestFor_intInnit(callableToTest: Callable[[Iterable[int], str | None, type
|
|
|
229
231
|
([21+0j, 34+0j], [21, 34])
|
|
230
232
|
]
|
|
231
233
|
for inputData, expected in testCases:
|
|
232
|
-
assert callableToTest(inputData, 'test', None) == expected
|
|
234
|
+
assert callableToTest(inputData, 'test', None) == expected
|
|
233
235
|
|
|
234
236
|
def testRejectsInvalidComplex() -> None:
|
|
235
237
|
for invalidComplex in [13+1j, 21+0.5j, 34.5+0j]:
|
|
236
238
|
with pytest.raises(ValueError):
|
|
237
|
-
callableToTest([invalidComplex], 'test', None)
|
|
239
|
+
callableToTest([invalidComplex], 'test', None)
|
|
238
240
|
|
|
239
241
|
return [
|
|
240
242
|
('testHandlesValidIntegers', testHandlesValidIntegers),
|
|
@@ -250,7 +252,7 @@ def PytestFor_intInnit(callableToTest: Callable[[Iterable[int], str | None, type
|
|
|
250
252
|
('testRejectsInvalidComplex', testRejectsInvalidComplex)
|
|
251
253
|
]
|
|
252
254
|
|
|
253
|
-
def PytestFor_oopsieKwargsie(callableToTest: Callable[[str], bool | None | str] = oopsieKwargsie) -> list[tuple[str, Callable[[], None]]]:
|
|
255
|
+
def PytestFor_oopsieKwargsie(callableToTest: Callable[[str], bool | None | str] = oopsieKwargsie) -> list[tuple[str, Callable[[], None]]]:
|
|
254
256
|
"""Return a list of test functions to validate string-to-boolean/None conversion behavior.
|
|
255
257
|
|
|
256
258
|
This function provides a comprehensive test suite for validating string parsing and conversion
|
|
@@ -312,10 +314,10 @@ def PytestFor_oopsieKwargsie(callableToTest: Callable[[str], bool | None | str]
|
|
|
312
314
|
message = "Cannot be stringified"
|
|
313
315
|
raise TypeError(message)
|
|
314
316
|
|
|
315
|
-
assert callableToTest(123) == "123"
|
|
317
|
+
assert callableToTest(123) == "123"
|
|
316
318
|
|
|
317
319
|
neverGonnaStringIt = NeverGonnaStringIt()
|
|
318
|
-
result = callableToTest(neverGonnaStringIt)
|
|
320
|
+
result = callableToTest(neverGonnaStringIt)
|
|
319
321
|
assert result is neverGonnaStringIt
|
|
320
322
|
|
|
321
323
|
return [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hunterMakesPy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Easy Python functions making making functional Python functions easier.
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -9,7 +9,7 @@ Project-URL: Homepage, https://github.com/hunterhogan/hunterMakesPy
|
|
|
9
9
|
Project-URL: Issues, https://github.com/hunterhogan/hunterMakesPy/issues
|
|
10
10
|
Project-URL: Repository, https://github.com/hunterhogan/hunterMakesPy
|
|
11
11
|
Keywords: attribute loading,concurrency limit,configuration,defensive programming,dictionary merging,directory creation,dynamic import,error propagation,file system utilities,input validation,integer parsing,module loading,nested data structures,package settings,parameter validation,pytest,string extraction,test utilities
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Environment :: Console
|
|
14
14
|
Classifier: Framework :: Pytest
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -30,12 +30,12 @@ Classifier: Typing :: Typed
|
|
|
30
30
|
Requires-Python: >=3.11
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
License-File: LICENSE
|
|
33
|
+
Requires-Dist: autoflake
|
|
33
34
|
Requires-Dist: charset_normalizer
|
|
35
|
+
Requires-Dist: isort
|
|
34
36
|
Requires-Dist: more_itertools
|
|
35
37
|
Requires-Dist: numpy
|
|
36
38
|
Provides-Extra: development
|
|
37
|
-
Requires-Dist: mypy; extra == "development"
|
|
38
|
-
Requires-Dist: pyupgrade; extra == "development"
|
|
39
39
|
Requires-Dist: setuptools-scm; extra == "development"
|
|
40
40
|
Provides-Extra: testing
|
|
41
41
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -164,13 +164,4 @@ def test_myFunction(nameOfTest, callablePytest):
|
|
|
164
164
|
[](https://HunterThinks.com/support)
|
|
165
165
|
[](https://www.youtube.com/@HunterHogan)
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Coding One Step at a Time:
|
|
170
|
-
|
|
171
|
-
0. WRITE CODE.
|
|
172
|
-
1. Don't write stupid code that's hard to revise.
|
|
173
|
-
2. Write good code.
|
|
174
|
-
3. When revising, write better code.
|
|
175
|
-
|
|
176
|
-
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
167
|
+
[](https://creativecommons.org/licenses/by-nc/4.0/)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
hunterMakesPy/__init__.py,sha256=N2Bvmacvs9z8ITWWjQr6-5LQHZ1FG5KL6v-pR1m6fCY,1547
|
|
2
|
+
hunterMakesPy/_theSSOT.py,sha256=x9Rdmw0qeAqgmlMFyFYRTRV5kEDYXcN4aBZ4KjlnKEU,170
|
|
3
|
+
hunterMakesPy/coping.py,sha256=42a_1kB6zHeRpfbpPnjmhrgWTPvUtqE5W9z3tqu-K8w,6068
|
|
4
|
+
hunterMakesPy/dataStructures.py,sha256=0-BRFriADFKX9pvjYj03ukj021ZII61wet2Wq2yAEdM,11776
|
|
5
|
+
hunterMakesPy/filesystemToolkit.py,sha256=8Yx8SH56w7g9wxpLjsdCaC1RvsW8Ur_cDrxhHaY9cLM,6848
|
|
6
|
+
hunterMakesPy/parseParameters.py,sha256=uQXoD89BfWAmRVT2yabyHtW7qITwAuCC9eh0sQFIV5Q,11966
|
|
7
|
+
hunterMakesPy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
hunterMakesPy/pytestForYourUse.py,sha256=GiN1C1gTTM0ZunRPEMnrKlLQLMdH0wF_ZGr_RPgRjjA,500
|
|
9
|
+
hunterMakesPy/theTypes.py,sha256=C2d0uLn1VIx6_2CK41it3IP7iplSQqe51tzWc-RT320,306
|
|
10
|
+
hunterMakesPy/tests/__init__.py,sha256=C_FzfKDi_VrGVxlenWHyOYtKShAKlt3KW14jeRx1mQI,224
|
|
11
|
+
hunterMakesPy/tests/conftest.py,sha256=NZQPRiwvGhP16hJ6WGGm9eKLxfQArYV8E9X12YzSpP0,2827
|
|
12
|
+
hunterMakesPy/tests/test_coping.py,sha256=mH89TUAL6fJanBLlhdVlCNNQqm5OpdcQMP_p5W2JJwo,9860
|
|
13
|
+
hunterMakesPy/tests/test_dataStructures.py,sha256=OouddHjN-Km26U92jYwnjYeP6_Y2DrJLgq3qD_8GvGw,16393
|
|
14
|
+
hunterMakesPy/tests/test_filesystemToolkit.py,sha256=_CoSMzstJwWZ_tkNyIqclOIIqTaY2tYfUIgxGFfC0Jk,15335
|
|
15
|
+
hunterMakesPy/tests/test_parseParameters.py,sha256=UKf1hwhLtXYL96VVJNf8NpJOyzRLO204pd-9PdDjufA,13267
|
|
16
|
+
huntermakespy-0.3.1.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
17
|
+
huntermakespy-0.3.1.dist-info/METADATA,sha256=_rbA9J-buuKyZvp8vkiYMhSipTCPdLFGdjRsRNtK5oU,6319
|
|
18
|
+
huntermakespy-0.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
huntermakespy-0.3.1.dist-info/top_level.txt,sha256=Uh4bj8EDTdsRpqY1VlK_his_B4HDfZ6Tqrwhoj75P_w,14
|
|
20
|
+
huntermakespy-0.3.1.dist-info/RECORD,,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
hunterMakesPy/__init__.py,sha256=bVF1F2Mdo5AOiioEfxKvNrnsa3vCFI16eMK7Oy5O5TU,1450
|
|
2
|
-
hunterMakesPy/_theSSOT.py,sha256=lkLOG3oTIWNKD_ULX55chlUGNqCHgqVIrBvolvK1vbQ,153
|
|
3
|
-
hunterMakesPy/coping.py,sha256=7NBwaGutEr6Q-2mIz65M69NkrbpG24u1I5HXx6VaAWI,6057
|
|
4
|
-
hunterMakesPy/dataStructures.py,sha256=7CxCBpQmHJzGxTq_AfkAeh2QdJDkzBr5lfSpPaA1GkE,11722
|
|
5
|
-
hunterMakesPy/filesystemToolkit.py,sha256=jd7H5UtrIrPiCYWcvNBNa6DAy-2Ewcf21-jbGXg_IVI,4702
|
|
6
|
-
hunterMakesPy/parseParameters.py,sha256=1mwGNVIZ1x23k_di3lhOhQb8QMXMUCHgt7xw3NRzDXs,11814
|
|
7
|
-
hunterMakesPy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
hunterMakesPy/pytestForYourUse.py,sha256=GiN1C1gTTM0ZunRPEMnrKlLQLMdH0wF_ZGr_RPgRjjA,500
|
|
9
|
-
hunterMakesPy/theTypes.py,sha256=C2d0uLn1VIx6_2CK41it3IP7iplSQqe51tzWc-RT320,306
|
|
10
|
-
hunterMakesPy/tests/__init__.py,sha256=C_FzfKDi_VrGVxlenWHyOYtKShAKlt3KW14jeRx1mQI,224
|
|
11
|
-
hunterMakesPy/tests/conftest.py,sha256=NZQPRiwvGhP16hJ6WGGm9eKLxfQArYV8E9X12YzSpP0,2827
|
|
12
|
-
hunterMakesPy/tests/test_coping.py,sha256=mH89TUAL6fJanBLlhdVlCNNQqm5OpdcQMP_p5W2JJwo,9860
|
|
13
|
-
hunterMakesPy/tests/test_dataStructures.py,sha256=OouddHjN-Km26U92jYwnjYeP6_Y2DrJLgq3qD_8GvGw,16393
|
|
14
|
-
hunterMakesPy/tests/test_filesystemToolkit.py,sha256=q2voXjCbQPIT8l8VF9iuWX1Bs2ZieABItWoVkITj_fo,8841
|
|
15
|
-
hunterMakesPy/tests/test_parseParameters.py,sha256=80npsoWcCackjxvoW2dMXMpHeale7fuRXyXp78MibLs,14037
|
|
16
|
-
huntermakespy-0.2.4.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
|
|
17
|
-
huntermakespy-0.2.4.dist-info/METADATA,sha256=UeGvfHoO_DMo_K5yy8daea6K7Ska8X6hHbC7MuCh7p8,6491
|
|
18
|
-
huntermakespy-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
-
huntermakespy-0.2.4.dist-info/top_level.txt,sha256=Uh4bj8EDTdsRpqY1VlK_his_B4HDfZ6Tqrwhoj75P_w,14
|
|
20
|
-
huntermakespy-0.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|