hunterMakesPy 0.3.3__py3-none-any.whl → 0.4.0__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/Z0Z_CallableFunction.py +70 -0
- hunterMakesPy/__init__.py +8 -1
- hunterMakesPy/coping.py +2 -4
- hunterMakesPy/pytestForYourUse.py +1 -2
- hunterMakesPy/tests/conftest.py +79 -5
- hunterMakesPy/tests/test_coping.py +258 -153
- hunterMakesPy/tests/test_dataStructures.py +250 -69
- hunterMakesPy/tests/test_filesystemToolkit.py +245 -131
- hunterMakesPy/tests/test_parseParameters.py +63 -7
- hunterMakesPy/tests/test_theTypes.py +359 -0
- hunterMakesPy/theTypes.py +5 -6
- {huntermakespy-0.3.3.dist-info → huntermakespy-0.4.0.dist-info}/METADATA +2 -3
- huntermakespy-0.4.0.dist-info/RECORD +22 -0
- {huntermakespy-0.3.3.dist-info → huntermakespy-0.4.0.dist-info}/WHEEL +1 -1
- huntermakespy-0.3.3.dist-info/RECORD +0 -20
- {huntermakespy-0.3.3.dist-info → huntermakespy-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {huntermakespy-0.3.3.dist-info → huntermakespy-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Protocols for callable functions with full type safety."""
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from types import CellType, CodeType, MethodType
|
|
4
|
+
from typing import Any, overload, ParamSpec, Protocol, runtime_checkable, Self, TypeVar, TypeVarTuple
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
#======== Stolen, uh, I mean copied from typeshed:stdlib\_typeshed\__init__.pyi ========
|
|
8
|
+
type AnnotationForm = Any
|
|
9
|
+
|
|
10
|
+
if sys.version_info >= (3, 14):
|
|
11
|
+
from annotationlib import Format
|
|
12
|
+
|
|
13
|
+
# NOTE These return annotations, which can be arbitrary objects
|
|
14
|
+
type AnnotateFunc = Callable[[Format], dict[str, AnnotationForm]]
|
|
15
|
+
type EvaluateFunc = Callable[[Format], AnnotationForm]
|
|
16
|
+
#======== End stolen, uh, I mean copied from typeshed:stdlib\_typeshed\__init__.pyi ========
|
|
17
|
+
|
|
18
|
+
@runtime_checkable
|
|
19
|
+
class CallableFunction[**P, R](Protocol):
|
|
20
|
+
"""A Protocol representing callable functions with descriptor support.
|
|
21
|
+
|
|
22
|
+
Mimics types.FunctionType while being a drop-in replacement for `collections.abc.Callable`. Includes all standard function
|
|
23
|
+
attributes and the descriptor protocol for proper method binding.
|
|
24
|
+
|
|
25
|
+
Note: @runtime_checkable only validates attribute presence, not signatures.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
# NOTE: The eehhhh, IDK... section
|
|
29
|
+
__doc__: str | None
|
|
30
|
+
__wrapped__: Any # For functools.wraps support
|
|
31
|
+
# NOTE: End eehhhh, IDK... section
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def __closure__(self) -> tuple[CellType, ...] | None:
|
|
35
|
+
"""Tuple of cells that contain bindings for the function's free variables."""
|
|
36
|
+
...
|
|
37
|
+
__code__: CodeType
|
|
38
|
+
__defaults__: tuple[Any, ...] | None
|
|
39
|
+
__dict__: dict[str, Any]
|
|
40
|
+
@property
|
|
41
|
+
def __globals__(self) -> dict[str, Any]:
|
|
42
|
+
"""The global namespace in which the function was defined."""
|
|
43
|
+
...
|
|
44
|
+
__name__: str
|
|
45
|
+
__qualname__: str
|
|
46
|
+
__annotations__: dict[str, AnnotationForm]
|
|
47
|
+
if sys.version_info >= (3, 14):
|
|
48
|
+
__annotate__: AnnotateFunc | None
|
|
49
|
+
__kwdefaults__: dict[str, Any] | None
|
|
50
|
+
@property
|
|
51
|
+
def __builtins__(self) -> dict[str, Any]:
|
|
52
|
+
"""The built-in namespace in which the function was defined."""
|
|
53
|
+
...
|
|
54
|
+
__type_params__: tuple[TypeVar | ParamSpec | TypeVarTuple, ...]
|
|
55
|
+
__module__: str
|
|
56
|
+
|
|
57
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
58
|
+
"""Execute the callable with the given arguments."""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
@overload
|
|
62
|
+
def __get__(self, instance: None, owner: type, /) -> Self: ...
|
|
63
|
+
@overload
|
|
64
|
+
def __get__(self, instance: object, owner: type | None = None, /) -> MethodType: ...
|
|
65
|
+
def __get__(self, instance: object | None, owner: type | None = None) -> Self | MethodType:
|
|
66
|
+
"""Descriptor protocol for method binding.
|
|
67
|
+
|
|
68
|
+
Returns self when accessed on the class, or a bound MethodType when accessed on an instance.
|
|
69
|
+
"""
|
|
70
|
+
...
|
hunterMakesPy/__init__.py
CHANGED
|
@@ -8,15 +8,22 @@ This package provides:
|
|
|
8
8
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# isort: split
|
|
12
|
+
from hunterMakesPy.theTypes import (
|
|
13
|
+
CallableFunction as CallableFunction, identifierDotAttribute as identifierDotAttribute, Ordinals as Ordinals)
|
|
12
14
|
|
|
15
|
+
# isort: split
|
|
13
16
|
from hunterMakesPy.coping import PackageSettings as PackageSettings, raiseIfNone as raiseIfNone
|
|
14
17
|
|
|
18
|
+
# isort: split
|
|
15
19
|
from hunterMakesPy.parseParameters import defineConcurrencyLimit, intInnit, oopsieKwargsie
|
|
16
20
|
|
|
21
|
+
# isort: split
|
|
17
22
|
from hunterMakesPy.filesystemToolkit import (
|
|
18
23
|
importLogicalPath2Identifier, importPathFilename2Identifier, makeDirsSafely, writePython, writeStringToHere)
|
|
19
24
|
|
|
25
|
+
# isort: split
|
|
20
26
|
from hunterMakesPy.dataStructures import autoDecodingRLE, stringItUp, updateExtendPolishDictionaryLists
|
|
21
27
|
|
|
28
|
+
# isort: split
|
|
22
29
|
from hunterMakesPy._theSSOT import settingsPackage # pyright: ignore[reportUnusedImport]
|
hunterMakesPy/coping.py
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
from importlib.util import find_spec
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from tomllib import loads as tomllib_loads
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
import dataclasses
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from importlib.machinery import ModuleSpec
|
|
10
10
|
|
|
11
|
-
TypeSansNone = TypeVar('TypeSansNone')
|
|
12
|
-
|
|
13
11
|
def getIdentifierPackagePACKAGING(identifierPackageFALLBACK: str) -> str:
|
|
14
12
|
"""Get package name from pyproject.toml or fallback to provided value."""
|
|
15
13
|
try:
|
|
@@ -85,7 +83,7 @@ class PackageSettings:
|
|
|
85
83
|
if self.pathPackage == Path() and self.identifierPackage:
|
|
86
84
|
self.pathPackage = getPathPackageINSTALLING(self.identifierPackage)
|
|
87
85
|
|
|
88
|
-
def raiseIfNone(expression: TypeSansNone | None, errorMessage: str | None = None) -> TypeSansNone:
|
|
86
|
+
def raiseIfNone[TypeSansNone](expression: TypeSansNone | None, errorMessage: str | None = None) -> TypeSansNone:
|
|
89
87
|
"""Convert the `expression` return annotation from '`cerPytainty | None`' to '`cerPytainty`' because `expression` cannot be `None`; `raise` an `Exception` if you're wrong.
|
|
90
88
|
|
|
91
89
|
The Python interpreter evaluates `expression` to a value: think of a function call or an attribute access. You can use
|
|
@@ -6,5 +6,4 @@ Note: These test functions are now in `hunterMakesPy.tests` with all other tests
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from hunterMakesPy.tests.test_parseParameters import (
|
|
9
|
-
PytestFor_defineConcurrencyLimit
|
|
10
|
-
PytestFor_oopsieKwargsie as PytestFor_oopsieKwargsie)
|
|
9
|
+
PytestFor_defineConcurrencyLimit, PytestFor_intInnit, PytestFor_oopsieKwargsie)
|
hunterMakesPy/tests/conftest.py
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
"""Configuration and fixtures for pytest.
|
|
2
|
+
|
|
3
|
+
(AI generated docstring)
|
|
4
|
+
|
|
5
|
+
This module provides shared fixtures and utility functions for the test suite,
|
|
6
|
+
including data paths, source code samples, and standardized assertion helpers.
|
|
7
|
+
|
|
8
|
+
"""
|
|
1
9
|
# pyright: standard
|
|
2
10
|
from collections.abc import Callable
|
|
3
11
|
from typing import Any
|
|
@@ -6,11 +14,26 @@ import pathlib
|
|
|
6
14
|
import pytest
|
|
7
15
|
|
|
8
16
|
# SSOT for test data paths and filenames
|
|
9
|
-
pathDataSamples = pathlib.Path("hunterMakesPy/tests/dataSamples")
|
|
17
|
+
pathDataSamples: pathlib.Path = pathlib.Path("hunterMakesPy/tests/dataSamples")
|
|
10
18
|
|
|
11
19
|
# Fixture to provide a temporary directory for filesystem tests
|
|
12
20
|
@pytest.fixture
|
|
13
21
|
def pathTmpTesting(tmp_path: pathlib.Path) -> pathlib.Path:
|
|
22
|
+
"""Provide a temporary directory for filesystem tests.
|
|
23
|
+
|
|
24
|
+
(AI generated docstring)
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
tmp_path : pathlib.Path
|
|
29
|
+
The pytest built-in temporary path fixture.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
pathTmpTesting : pathlib.Path
|
|
34
|
+
The path to the temporary directory.
|
|
35
|
+
|
|
36
|
+
"""
|
|
14
37
|
return tmp_path
|
|
15
38
|
|
|
16
39
|
# Fixture for predictable Python source code samples
|
|
@@ -44,7 +67,27 @@ def listFileContentsFibonacci() -> list[str]:
|
|
|
44
67
|
return ['fibonacci8', 'fibonacci13', 'fibonacci21', 'fibonacci34']
|
|
45
68
|
|
|
46
69
|
def uniformTestFailureMessage(expected: Any, actual: Any, functionName: str, *arguments: Any, **keywordArguments: Any) -> str:
|
|
47
|
-
"""Format assertion message for any test comparison.
|
|
70
|
+
"""Format assertion message for any test comparison.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
expected : Any
|
|
75
|
+
The expected value or outcome.
|
|
76
|
+
actual : Any
|
|
77
|
+
The actual value or outcome received.
|
|
78
|
+
functionName : str
|
|
79
|
+
The name of the function or test case being executed.
|
|
80
|
+
*arguments : Any
|
|
81
|
+
Positional arguments passed to the function having its return value checked.
|
|
82
|
+
**keywordArguments : Any
|
|
83
|
+
Keyword arguments passed to the function having its return value checked.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
message : str
|
|
88
|
+
A formatted failure message detailing the expectation vs reality.
|
|
89
|
+
|
|
90
|
+
"""
|
|
48
91
|
listArgumentComponents: list[str] = [str(parameter) for parameter in arguments]
|
|
49
92
|
listKeywordComponents: list[str] = [f"{key}={value}" for key, value in keywordArguments.items()]
|
|
50
93
|
joinedArguments: str = ', '.join(listArgumentComponents + listKeywordComponents)
|
|
@@ -54,7 +97,22 @@ def uniformTestFailureMessage(expected: Any, actual: Any, functionName: str, *ar
|
|
|
54
97
|
f"Got: {actual}")
|
|
55
98
|
|
|
56
99
|
def standardizedEqualTo(expected: Any, functionTarget: Callable[..., Any], *arguments: Any, **keywordArguments: Any) -> None:
|
|
57
|
-
"""Template for most tests to compare
|
|
100
|
+
"""Template for most tests to compare actual outcome with expected outcome.
|
|
101
|
+
|
|
102
|
+
Includes handling for expected errors/exceptions.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
expected : Any
|
|
107
|
+
The expected return value, or an Exception type if an error is expected.
|
|
108
|
+
functionTarget : Callable[..., Any]
|
|
109
|
+
The function to call and test.
|
|
110
|
+
*arguments : Any
|
|
111
|
+
Positional arguments to pass to `functionTarget`.
|
|
112
|
+
**keywordArguments : Any
|
|
113
|
+
Keyword arguments to pass to `functionTarget`.
|
|
114
|
+
|
|
115
|
+
"""
|
|
58
116
|
if type(expected) == type[Exception]: # noqa: E721
|
|
59
117
|
messageExpected: str = expected.__name__
|
|
60
118
|
else:
|
|
@@ -63,7 +121,23 @@ def standardizedEqualTo(expected: Any, functionTarget: Callable[..., Any], *argu
|
|
|
63
121
|
try:
|
|
64
122
|
messageActual = actual = functionTarget(*arguments, **keywordArguments)
|
|
65
123
|
except Exception as actualError:
|
|
66
|
-
messageActual
|
|
124
|
+
messageActual = type(actualError).__name__
|
|
67
125
|
actual = type(actualError)
|
|
68
126
|
|
|
69
|
-
|
|
127
|
+
functionName: str = getattr(functionTarget, "__name__", functionTarget.__class__.__name__)
|
|
128
|
+
assert actual == expected, uniformTestFailureMessage(messageExpected, messageActual, functionName, *arguments, **keywordArguments)
|
|
129
|
+
|
|
130
|
+
# Why I wish I could figure out how to implement standardized* test functions.
|
|
131
|
+
# ruff: noqa: ERA001
|
|
132
|
+
# standardizedEqualTo(expected, updateExtendPolishDictionaryLists, *value_dictionaryLists, **keywordArguments)
|
|
133
|
+
# NOTE one line of code with `standardizedEqualTo` (above) replaced the following ten lines of code. Use `standardizedEqualTo`.
|
|
134
|
+
# if isinstance(expected, type) and issubclass(expected, Exception):
|
|
135
|
+
# with pytest.raises(expected):
|
|
136
|
+
# updateExtendPolishDictionaryLists(*value_dictionaryLists, **keywordArguments)
|
|
137
|
+
# else:
|
|
138
|
+
# result = updateExtendPolishDictionaryLists(*value_dictionaryLists, **keywordArguments)
|
|
139
|
+
# if description == "Set values": # Special handling for unordered sets
|
|
140
|
+
# for key in result:
|
|
141
|
+
# assert sorted(result[key]) == sorted(expected[key])
|
|
142
|
+
# else:
|
|
143
|
+
# assert result == expected
|