hunterMakesPy 0.1.1__tar.gz → 0.1.2__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.
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/PKG-INFO +10 -11
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/__init__.py +1 -3
- huntermakespy-0.1.2/hunterMakesPy/_theSSOT.py +4 -0
- huntermakespy-0.1.2/hunterMakesPy/coping.py +149 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/dataStructures.py +2 -2
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy.egg-info/PKG-INFO +10 -11
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy.egg-info/requires.txt +5 -4
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/pyproject.toml +20 -21
- huntermakespy-0.1.2/tests/test_coping.py +216 -0
- huntermakespy-0.1.1/hunterMakesPy/_theSSOT.py +0 -40
- huntermakespy-0.1.1/hunterMakesPy/coping.py +0 -73
- huntermakespy-0.1.1/tests/test_coping.py +0 -56
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/LICENSE +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/README.md +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/filesystemToolkit.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/parseParameters.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/py.typed +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/pytestForYourUse.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy/theTypes.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy.egg-info/SOURCES.txt +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy.egg-info/dependency_links.txt +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/hunterMakesPy.egg-info/top_level.txt +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/setup.cfg +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/tests/__init__.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/tests/conftest.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/tests/test_dataStructures.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/tests/test_filesystemToolkit.py +0 -0
- {huntermakespy-0.1.1 → huntermakespy-0.1.2}/tests/test_parseParameters.py +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hunterMakesPy
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.1.2
|
|
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
|
|
7
7
|
Project-URL: Donate, https://www.patreon.com/integrated
|
|
8
8
|
Project-URL: Homepage, https://github.com/hunterhogan/
|
|
9
9
|
Project-URL: Issues, https://github.com/hunterhogan/
|
|
10
10
|
Project-URL: Repository, https://github.com/hunterhogan/
|
|
11
|
-
Keywords: defensive programming,
|
|
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
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Environment :: Console
|
|
14
14
|
Classifier: Framework :: Pytest
|
|
@@ -17,31 +17,30 @@ Classifier: Intended Audience :: End Users/Desktop
|
|
|
17
17
|
Classifier: Intended Audience :: Other Audience
|
|
18
18
|
Classifier: Natural Language :: English
|
|
19
19
|
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
-
Classifier: Programming Language :: Python :: 3
|
|
25
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
|
-
Classifier: Programming Language :: Python
|
|
27
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
27
|
Classifier: Topic :: Utilities
|
|
29
28
|
Classifier: Typing :: Typed
|
|
30
|
-
Requires-Python: >=3.
|
|
29
|
+
Requires-Python: >=3.11
|
|
31
30
|
Description-Content-Type: text/markdown
|
|
32
31
|
License-File: LICENSE
|
|
33
32
|
Requires-Dist: charset_normalizer
|
|
34
33
|
Requires-Dist: more_itertools
|
|
35
34
|
Requires-Dist: numpy
|
|
36
35
|
Requires-Dist: python_minifier
|
|
37
|
-
|
|
36
|
+
Provides-Extra: development
|
|
37
|
+
Requires-Dist: mypy; extra == "development"
|
|
38
|
+
Requires-Dist: pyupgrade; extra == "development"
|
|
39
|
+
Requires-Dist: setuptools-scm; extra == "development"
|
|
38
40
|
Provides-Extra: testing
|
|
39
|
-
Requires-Dist: mypy; extra == "testing"
|
|
40
41
|
Requires-Dist: pytest; extra == "testing"
|
|
41
42
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
42
43
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
43
|
-
Requires-Dist: pyupgrade; extra == "testing"
|
|
44
|
-
Requires-Dist: setuptools-scm; extra == "testing"
|
|
45
44
|
Dynamic: license-file
|
|
46
45
|
|
|
47
46
|
# hunterMakesPy
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""A modular toolkit for defensive programming, parameter validation, file system utilities, and data structure manipulation.
|
|
2
2
|
|
|
3
|
-
(AI generated docstring)
|
|
4
|
-
|
|
5
3
|
This package provides:
|
|
6
4
|
- Defensive programming helpers for handling `None` values and error propagation.
|
|
7
5
|
- Parameter and input validation, integer parsing, and concurrency limit utilities.
|
|
@@ -11,7 +9,7 @@ This package provides:
|
|
|
11
9
|
"""
|
|
12
10
|
from hunterMakesPy.theTypes import identifierDotAttribute as identifierDotAttribute
|
|
13
11
|
|
|
14
|
-
from hunterMakesPy.coping import raiseIfNone as raiseIfNone
|
|
12
|
+
from hunterMakesPy.coping import PackageSettings as PackageSettings, raiseIfNone as raiseIfNone
|
|
15
13
|
|
|
16
14
|
from hunterMakesPy.parseParameters import (defineConcurrencyLimit as defineConcurrencyLimit, intInnit as intInnit,
|
|
17
15
|
oopsieKwargsie as oopsieKwargsie)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Package configuration and defensive programming utilities for Python projects."""
|
|
2
|
+
from importlib.util import find_spec
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tomllib import loads as tomllib_loads
|
|
5
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
6
|
+
import dataclasses
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from importlib.machinery import ModuleSpec
|
|
10
|
+
|
|
11
|
+
TypeSansNone = TypeVar('TypeSansNone')
|
|
12
|
+
|
|
13
|
+
def getIdentifierPackagePACKAGING(identifierPackageFALLBACK: str) -> str:
|
|
14
|
+
"""Get package name from pyproject.toml or fallback to provided value."""
|
|
15
|
+
try:
|
|
16
|
+
return tomllib_loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project']['name']
|
|
17
|
+
except Exception: # noqa: BLE001
|
|
18
|
+
return identifierPackageFALLBACK
|
|
19
|
+
|
|
20
|
+
def getPathPackageINSTALLING(identifierPackage: str) -> Path:
|
|
21
|
+
"""Return the root directory of the installed package."""
|
|
22
|
+
try:
|
|
23
|
+
moduleSpecification: ModuleSpec | None = find_spec(identifierPackage)
|
|
24
|
+
if moduleSpecification and moduleSpecification.origin:
|
|
25
|
+
pathFilename = Path(moduleSpecification.origin)
|
|
26
|
+
return pathFilename.parent if pathFilename.is_file() else pathFilename
|
|
27
|
+
except ModuleNotFoundError:
|
|
28
|
+
pass
|
|
29
|
+
return Path.cwd()
|
|
30
|
+
|
|
31
|
+
@dataclasses.dataclass
|
|
32
|
+
class PackageSettings:
|
|
33
|
+
"""Configuration container for Python package metadata and runtime settings.
|
|
34
|
+
|
|
35
|
+
This `class` provides a simple way to store and access basic information about a Python package, It will automatically resolve
|
|
36
|
+
package identifiers and installation paths if they are not passed to the `class` constructor. Python `dataclasses` are easy to
|
|
37
|
+
subtype and extend.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
identifierPackageFALLBACK : str = ''
|
|
42
|
+
Fallback package identifier used only during initialization when automatic discovery fails.
|
|
43
|
+
pathPackage : Path = Path()
|
|
44
|
+
Absolute path to the installed package directory. Automatically resolved from `identifierPackage` if not provided.
|
|
45
|
+
identifierPackage : str = ''
|
|
46
|
+
Canonical name of the package. Automatically extracted from `pyproject.toml`.
|
|
47
|
+
fileExtension : str = '.py'
|
|
48
|
+
Default file extension.
|
|
49
|
+
|
|
50
|
+
Examples
|
|
51
|
+
--------
|
|
52
|
+
Automatic package discovery from development environment:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
settings = PackageSettings(identifierPackageFALLBACK='cobraPy')
|
|
56
|
+
# Automatically discovers package name from pyproject.toml
|
|
57
|
+
# Resolves installation path from package identifier
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Explicit configuration for specific deployment:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
settings = PackageSettings(
|
|
64
|
+
identifierPackage='cobraPy',
|
|
65
|
+
pathPackage=Path('/opt/tenEx/packages/cobraPy'),
|
|
66
|
+
fileExtension='.pyx'
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
identifierPackageFALLBACK: dataclasses.InitVar[str] = ''
|
|
73
|
+
"""Fallback package identifier used during initialization only."""
|
|
74
|
+
pathPackage: Path = dataclasses.field(default_factory=Path, metadata={'evaluateWhen': 'installing'})
|
|
75
|
+
"""Absolute path to the installed package."""
|
|
76
|
+
identifierPackage: str = dataclasses.field(default='', metadata={'evaluateWhen': 'packaging'})
|
|
77
|
+
"""Name of this package."""
|
|
78
|
+
fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
|
|
79
|
+
"""Default file extension for files."""
|
|
80
|
+
|
|
81
|
+
def __post_init__(self, identifierPackageFALLBACK: str) -> None:
|
|
82
|
+
"""Initialize computed fields after dataclass initialization."""
|
|
83
|
+
if not self.identifierPackage and identifierPackageFALLBACK:
|
|
84
|
+
self.identifierPackage = getIdentifierPackagePACKAGING(identifierPackageFALLBACK)
|
|
85
|
+
if self.pathPackage == Path() and self.identifierPackage:
|
|
86
|
+
self.pathPackage = getPathPackageINSTALLING(self.identifierPackage)
|
|
87
|
+
|
|
88
|
+
def raiseIfNone(returnTarget: TypeSansNone | None, errorMessage: str | None = None) -> TypeSansNone:
|
|
89
|
+
"""Raise a `ValueError` if the target value is `None`, otherwise return the value: tell the type checker that the return value is not `None`.
|
|
90
|
+
|
|
91
|
+
(AI generated docstring)
|
|
92
|
+
|
|
93
|
+
This is a defensive programming function that converts unexpected `None` values into explicit errors with context. It is useful for asserting that functions that might return `None` have actually returned a meaningful value.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
returnTarget : TypeSansNone | None
|
|
98
|
+
The value to check for `None`. If not `None`, this value is returned unchanged.
|
|
99
|
+
errorMessage : str | None = None
|
|
100
|
+
Custom error message to include in the `ValueError`. If `None`, a default message with debugging hints is used.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
returnTarget : TypeSansNone
|
|
105
|
+
The original `returnTarget` value, guaranteed to not be `None`.
|
|
106
|
+
|
|
107
|
+
Raises
|
|
108
|
+
------
|
|
109
|
+
ValueError
|
|
110
|
+
If `returnTarget` is `None`.
|
|
111
|
+
|
|
112
|
+
Examples
|
|
113
|
+
--------
|
|
114
|
+
Ensure a function result is not `None`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
def findFirstMatch(listItems: list[str], pattern: str) -> str | None:
|
|
118
|
+
for item in listItems:
|
|
119
|
+
if pattern in item:
|
|
120
|
+
return item
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
listFiles = ['document.txt', 'image.png', 'data.csv']
|
|
124
|
+
filename = raiseIfNone(findFirstMatch(listFiles, '.txt'))
|
|
125
|
+
# Returns 'document.txt'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Handle dictionary lookups with custom error messages:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
configurationMapping = {'host': 'localhost', 'port': 8080}
|
|
132
|
+
host = raiseIfNone(configurationMapping.get('host'),
|
|
133
|
+
"Configuration must include 'host' setting")
|
|
134
|
+
# Returns 'localhost'
|
|
135
|
+
|
|
136
|
+
# This would raise ValueError with custom message:
|
|
137
|
+
# database = raiseIfNone(configurationMapping.get('database'),
|
|
138
|
+
# "Configuration must include 'database' setting")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Thanks
|
|
142
|
+
------
|
|
143
|
+
sobolevn, https://github.com/sobolevn, for the seed of the function. https://github.com/python/typing/discussions/1997#discussioncomment-13108399
|
|
144
|
+
|
|
145
|
+
"""
|
|
146
|
+
if returnTarget is None:
|
|
147
|
+
message = errorMessage or 'A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`.'
|
|
148
|
+
raise ValueError(message)
|
|
149
|
+
return returnTarget
|
|
@@ -46,7 +46,7 @@ def autoDecodingRLE(arrayTarget: NDArray[integer[Any]], *, assumeAddSpaces: bool
|
|
|
46
46
|
"""
|
|
47
47
|
def sliceNDArrayToNestedLists(arraySlice: NDArray[integer[Any]]) -> Any:
|
|
48
48
|
def getLengthOption(optionAsStr: str) -> int:
|
|
49
|
-
|
|
49
|
+
"""`assumeAddSpaces` characters: `,` 1; `]*` 2."""
|
|
50
50
|
return assumeAddSpaces * (optionAsStr.count(',') + optionAsStr.count(']*') * 2) + len(optionAsStr)
|
|
51
51
|
|
|
52
52
|
if arraySlice.ndim > 1:
|
|
@@ -249,7 +249,7 @@ def updateExtendPolishDictionaryLists(*dictionaryLists: Mapping[str, list[Any] |
|
|
|
249
249
|
ImaStr = str(keyName)
|
|
250
250
|
ImaList = list(keyValue)
|
|
251
251
|
ePluribusUnum.setdefault(ImaStr, []).extend(ImaList)
|
|
252
|
-
except TypeError:
|
|
252
|
+
except TypeError:
|
|
253
253
|
if killErroneousDataTypes:
|
|
254
254
|
continue
|
|
255
255
|
else:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hunterMakesPy
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.1.2
|
|
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
|
|
7
7
|
Project-URL: Donate, https://www.patreon.com/integrated
|
|
8
8
|
Project-URL: Homepage, https://github.com/hunterhogan/
|
|
9
9
|
Project-URL: Issues, https://github.com/hunterhogan/
|
|
10
10
|
Project-URL: Repository, https://github.com/hunterhogan/
|
|
11
|
-
Keywords: defensive programming,
|
|
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
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Environment :: Console
|
|
14
14
|
Classifier: Framework :: Pytest
|
|
@@ -17,31 +17,30 @@ Classifier: Intended Audience :: End Users/Desktop
|
|
|
17
17
|
Classifier: Intended Audience :: Other Audience
|
|
18
18
|
Classifier: Natural Language :: English
|
|
19
19
|
Classifier: Operating System :: OS Independent
|
|
20
|
-
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
24
|
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
-
Classifier: Programming Language :: Python :: 3
|
|
25
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
|
-
Classifier: Programming Language :: Python
|
|
27
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
27
|
Classifier: Topic :: Utilities
|
|
29
28
|
Classifier: Typing :: Typed
|
|
30
|
-
Requires-Python: >=3.
|
|
29
|
+
Requires-Python: >=3.11
|
|
31
30
|
Description-Content-Type: text/markdown
|
|
32
31
|
License-File: LICENSE
|
|
33
32
|
Requires-Dist: charset_normalizer
|
|
34
33
|
Requires-Dist: more_itertools
|
|
35
34
|
Requires-Dist: numpy
|
|
36
35
|
Requires-Dist: python_minifier
|
|
37
|
-
|
|
36
|
+
Provides-Extra: development
|
|
37
|
+
Requires-Dist: mypy; extra == "development"
|
|
38
|
+
Requires-Dist: pyupgrade; extra == "development"
|
|
39
|
+
Requires-Dist: setuptools-scm; extra == "development"
|
|
38
40
|
Provides-Extra: testing
|
|
39
|
-
Requires-Dist: mypy; extra == "testing"
|
|
40
41
|
Requires-Dist: pytest; extra == "testing"
|
|
41
42
|
Requires-Dist: pytest-cov; extra == "testing"
|
|
42
43
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
43
|
-
Requires-Dist: pyupgrade; extra == "testing"
|
|
44
|
-
Requires-Dist: setuptools-scm; extra == "testing"
|
|
45
44
|
Dynamic: license-file
|
|
46
45
|
|
|
47
46
|
# hunterMakesPy
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "hunterMakesPy"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.1.2"
|
|
4
|
+
description = "Easy Python functions making making functional Python functions easier."
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
7
|
license = { 'text' = "CC-BY-NC-4.0" }
|
|
8
8
|
authors = [{ name = "Hunter Hogan", email = "HunterHogan@pm.me" }]
|
|
9
9
|
keywords = [
|
|
10
|
-
"
|
|
11
|
-
"parameter validation",
|
|
12
|
-
"input validation",
|
|
13
|
-
"error propagation",
|
|
10
|
+
"attribute loading",
|
|
14
11
|
"concurrency limit",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
12
|
+
"configuration",
|
|
13
|
+
"defensive programming",
|
|
14
|
+
"dictionary merging",
|
|
17
15
|
"directory creation",
|
|
18
16
|
"dynamic import",
|
|
17
|
+
"error propagation",
|
|
18
|
+
"file system utilities",
|
|
19
|
+
"input validation",
|
|
20
|
+
"integer parsing",
|
|
19
21
|
"module loading",
|
|
20
|
-
"attribute loading",
|
|
21
|
-
"string extraction",
|
|
22
22
|
"nested data structures",
|
|
23
|
-
"dictionary merging",
|
|
24
23
|
"package settings",
|
|
25
|
-
"
|
|
24
|
+
"parameter validation",
|
|
26
25
|
"pytest",
|
|
27
|
-
"
|
|
26
|
+
"string extraction",
|
|
27
|
+
"test utilities",
|
|
28
28
|
]
|
|
29
29
|
classifiers = [
|
|
30
30
|
"Development Status :: 4 - Beta",
|
|
@@ -35,13 +35,12 @@ classifiers = [
|
|
|
35
35
|
"Intended Audience :: Other Audience",
|
|
36
36
|
"Natural Language :: English",
|
|
37
37
|
"Operating System :: OS Independent",
|
|
38
|
-
"Programming Language :: Python
|
|
38
|
+
"Programming Language :: Python",
|
|
39
|
+
"Programming Language :: Python :: 3",
|
|
39
40
|
"Programming Language :: Python :: 3.11",
|
|
40
41
|
"Programming Language :: Python :: 3.12",
|
|
41
42
|
"Programming Language :: Python :: 3.13",
|
|
42
|
-
"Programming Language :: Python :: 3",
|
|
43
43
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
44
|
-
"Programming Language :: Python",
|
|
45
44
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
46
45
|
"Topic :: Utilities",
|
|
47
46
|
"Typing :: Typed",
|
|
@@ -52,15 +51,15 @@ dependencies = [
|
|
|
52
51
|
"more_itertools",
|
|
53
52
|
"numpy",
|
|
54
53
|
"python_minifier",
|
|
55
|
-
"tomli",
|
|
56
54
|
]
|
|
57
|
-
optional-dependencies = {
|
|
55
|
+
optional-dependencies = { development = [
|
|
58
56
|
"mypy",
|
|
57
|
+
"pyupgrade",
|
|
58
|
+
"setuptools-scm",
|
|
59
|
+
], testing = [
|
|
59
60
|
"pytest",
|
|
60
61
|
"pytest-cov",
|
|
61
62
|
"pytest-xdist",
|
|
62
|
-
"pyupgrade",
|
|
63
|
-
"setuptools-scm",
|
|
64
63
|
] }
|
|
65
64
|
|
|
66
65
|
[build-system]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from hunterMakesPy import PackageSettings, raiseIfNone
|
|
2
|
+
from hunterMakesPy.coping import getIdentifierPackagePACKAGING, getPathPackageINSTALLING
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from tests.conftest import uniformTestFailureMessage
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
"returnTarget, expected",
|
|
9
|
+
[
|
|
10
|
+
(13, 13),
|
|
11
|
+
(17, 17),
|
|
12
|
+
("fibonacci", "fibonacci"),
|
|
13
|
+
("prime", "prime"),
|
|
14
|
+
([], []),
|
|
15
|
+
({}, {}),
|
|
16
|
+
(False, False),
|
|
17
|
+
(0, 0),
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
def testRaiseIfNoneReturnsNonNoneValues(returnTarget: object, expected: object) -> None:
|
|
21
|
+
actual = raiseIfNone(returnTarget)
|
|
22
|
+
assert actual == expected, uniformTestFailureMessage(expected, actual, "testRaiseIfNoneReturnsNonNoneValues", returnTarget)
|
|
23
|
+
assert actual is returnTarget, uniformTestFailureMessage(returnTarget, actual, "testRaiseIfNoneReturnsNonNoneValues identity check", returnTarget)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def testRaiseIfNoneRaisesValueErrorWhenGivenNone() -> None:
|
|
27
|
+
with pytest.raises(ValueError, match="A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`."):
|
|
28
|
+
raiseIfNone(None)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize(
|
|
32
|
+
"customMessage",
|
|
33
|
+
[
|
|
34
|
+
"Configuration must include 'host' setting",
|
|
35
|
+
"Database connection failed",
|
|
36
|
+
"User input is required",
|
|
37
|
+
"Network request returned empty response",
|
|
38
|
+
]
|
|
39
|
+
)
|
|
40
|
+
def testRaiseIfNoneRaisesValueErrorWithCustomMessage(customMessage: str) -> None:
|
|
41
|
+
with pytest.raises(ValueError, match=customMessage):
|
|
42
|
+
raiseIfNone(None, customMessage)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def testRaiseIfNoneWithEmptyStringMessage() -> None:
|
|
46
|
+
with pytest.raises(ValueError, match="A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`."):
|
|
47
|
+
raiseIfNone(None, "")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def testRaiseIfNonePreservesTypeAnnotations() -> None:
|
|
51
|
+
integerValue: int = raiseIfNone(23)
|
|
52
|
+
assert isinstance(integerValue, int), uniformTestFailureMessage(int, type(integerValue), "testRaiseIfNonePreservesTypeAnnotations", integerValue)
|
|
53
|
+
|
|
54
|
+
stringValue: str = raiseIfNone("cardinal")
|
|
55
|
+
assert isinstance(stringValue, str), uniformTestFailureMessage(str, type(stringValue), "testRaiseIfNonePreservesTypeAnnotations", stringValue)
|
|
56
|
+
|
|
57
|
+
listValue: list[int] = raiseIfNone([29, 31])
|
|
58
|
+
assert isinstance(listValue, list), uniformTestFailureMessage(list, type(listValue), "testRaiseIfNonePreservesTypeAnnotations", listValue)
|
|
59
|
+
|
|
60
|
+
# Tests for PackageSettings dataclass
|
|
61
|
+
@pytest.mark.parametrize(
|
|
62
|
+
"identifierPackageFALLBACK, expectedIdentifierPackage",
|
|
63
|
+
[
|
|
64
|
+
("astToolFactory", "hunterMakesPy"), # Should read from pyproject.toml
|
|
65
|
+
("nonExistentPackage", "hunterMakesPy"), # Should read from pyproject.toml
|
|
66
|
+
("customPackage", "hunterMakesPy"), # Should read from pyproject.toml
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
def testPackageSettingsWithFallbackUsesProjectToml(identifierPackageFALLBACK: str, expectedIdentifierPackage: str) -> None:
|
|
70
|
+
"""Test that PackageSettings reads package name from pyproject.toml when using fallback."""
|
|
71
|
+
packageSettings = PackageSettings(identifierPackageFALLBACK)
|
|
72
|
+
assert packageSettings.identifierPackage == expectedIdentifierPackage, uniformTestFailureMessage(
|
|
73
|
+
expectedIdentifierPackage, packageSettings.identifierPackage, "PackageSettings fallback", identifierPackageFALLBACK
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
@pytest.mark.parametrize(
|
|
77
|
+
"explicitIdentifierPackage, expectedIdentifierPackage",
|
|
78
|
+
[
|
|
79
|
+
("customPackageName", "customPackageName"),
|
|
80
|
+
("fibonacci", "fibonacci"),
|
|
81
|
+
("prime", "prime"),
|
|
82
|
+
("astToolFactory", "astToolFactory"),
|
|
83
|
+
]
|
|
84
|
+
)
|
|
85
|
+
def testPackageSettingsWithExplicitIdentifierPackage(explicitIdentifierPackage: str, expectedIdentifierPackage: str) -> None:
|
|
86
|
+
"""Test that PackageSettings respects explicitly provided identifierPackage."""
|
|
87
|
+
packageSettings = PackageSettings(identifierPackage=explicitIdentifierPackage)
|
|
88
|
+
assert packageSettings.identifierPackage == expectedIdentifierPackage, uniformTestFailureMessage(
|
|
89
|
+
expectedIdentifierPackage, packageSettings.identifierPackage, "PackageSettings explicit identifierPackage", explicitIdentifierPackage
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@pytest.mark.parametrize(
|
|
93
|
+
"explicitPathPackage, expectedPathPackage",
|
|
94
|
+
[
|
|
95
|
+
(Path("C:/fibonacci/path"), Path("C:/fibonacci/path")),
|
|
96
|
+
(Path("C:/prime/directory"), Path("C:/prime/directory")),
|
|
97
|
+
(Path("/usr/local/lib/package"), Path("/usr/local/lib/package")),
|
|
98
|
+
(Path("relative/path"), Path("relative/path")),
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
def testPackageSettingsWithExplicitPathPackage(explicitPathPackage: Path, expectedPathPackage: Path) -> None:
|
|
102
|
+
"""Test that PackageSettings respects explicitly provided pathPackage."""
|
|
103
|
+
packageSettings = PackageSettings(pathPackage=explicitPathPackage)
|
|
104
|
+
assert packageSettings.pathPackage == expectedPathPackage, uniformTestFailureMessage(
|
|
105
|
+
expectedPathPackage, packageSettings.pathPackage, "PackageSettings explicit pathPackage", explicitPathPackage
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
@pytest.mark.parametrize(
|
|
109
|
+
"fileExtension, expectedFileExtension",
|
|
110
|
+
[
|
|
111
|
+
(".fibonacci", ".fibonacci"),
|
|
112
|
+
(".prime", ".prime"),
|
|
113
|
+
(".txt", ".txt"),
|
|
114
|
+
(".md", ".md"),
|
|
115
|
+
(".json", ".json"),
|
|
116
|
+
]
|
|
117
|
+
)
|
|
118
|
+
def testPackageSettingsWithCustomFileExtension(fileExtension: str, expectedFileExtension: str) -> None:
|
|
119
|
+
"""Test that PackageSettings respects custom file extensions."""
|
|
120
|
+
packageSettings = PackageSettings(fileExtension=fileExtension)
|
|
121
|
+
assert packageSettings.fileExtension == expectedFileExtension, uniformTestFailureMessage(
|
|
122
|
+
expectedFileExtension, packageSettings.fileExtension, "PackageSettings custom fileExtension", fileExtension
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def testPackageSettingsDefaultValues() -> None:
|
|
126
|
+
"""Test that PackageSettings has correct default values when no arguments provided."""
|
|
127
|
+
packageSettings = PackageSettings()
|
|
128
|
+
|
|
129
|
+
# Should have default file extension
|
|
130
|
+
assert packageSettings.fileExtension == '.py', uniformTestFailureMessage(
|
|
131
|
+
'.py', packageSettings.fileExtension, "PackageSettings default fileExtension"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# identifierPackage should be empty when no fallback provided
|
|
135
|
+
assert packageSettings.identifierPackage == '', uniformTestFailureMessage(
|
|
136
|
+
'', packageSettings.identifierPackage, "PackageSettings default identifierPackage"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# pathPackage should remain as Path() when identifierPackage is empty
|
|
140
|
+
expectedPath = Path()
|
|
141
|
+
assert packageSettings.pathPackage == expectedPath, uniformTestFailureMessage(
|
|
142
|
+
expectedPath, packageSettings.pathPackage, "PackageSettings default pathPackage"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@pytest.mark.parametrize(
|
|
146
|
+
"identifierPackageFALLBACK, identifierPackage, pathPackage, fileExtension",
|
|
147
|
+
[
|
|
148
|
+
("fallback", "custom", Path("C:/custom/path"), ".txt"),
|
|
149
|
+
("fibonacci", "prime", Path("C:/fibonacci/lib"), ".md"),
|
|
150
|
+
("defaultFallback", "overridePackage", Path("/usr/local/override"), ".json"),
|
|
151
|
+
]
|
|
152
|
+
)
|
|
153
|
+
def testPackageSettingsAllParametersCombined(
|
|
154
|
+
identifierPackageFALLBACK: str,
|
|
155
|
+
identifierPackage: str,
|
|
156
|
+
pathPackage: Path,
|
|
157
|
+
fileExtension: str
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Test PackageSettings with all parameters provided."""
|
|
160
|
+
packageSettings = PackageSettings(
|
|
161
|
+
identifierPackageFALLBACK,
|
|
162
|
+
identifierPackage=identifierPackage,
|
|
163
|
+
pathPackage=pathPackage,
|
|
164
|
+
fileExtension=fileExtension
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
assert packageSettings.identifierPackage == identifierPackage, uniformTestFailureMessage(
|
|
168
|
+
identifierPackage, packageSettings.identifierPackage, "PackageSettings combined identifierPackage"
|
|
169
|
+
)
|
|
170
|
+
assert packageSettings.pathPackage == pathPackage, uniformTestFailureMessage(
|
|
171
|
+
pathPackage, packageSettings.pathPackage, "PackageSettings combined pathPackage"
|
|
172
|
+
)
|
|
173
|
+
assert packageSettings.fileExtension == fileExtension, uniformTestFailureMessage(
|
|
174
|
+
fileExtension, packageSettings.fileExtension, "PackageSettings combined fileExtension"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def testPackageSettingsFallbackIgnoredWhenExplicitIdentifierProvided() -> None:
|
|
178
|
+
"""Test that fallback is ignored when explicit identifierPackage is provided."""
|
|
179
|
+
packageSettings = PackageSettings("shouldBeIgnored", identifierPackage="explicit")
|
|
180
|
+
assert packageSettings.identifierPackage == "explicit", uniformTestFailureMessage(
|
|
181
|
+
"explicit", packageSettings.identifierPackage, "PackageSettings fallback ignored"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Tests for helper functions
|
|
185
|
+
@pytest.mark.parametrize(
|
|
186
|
+
"identifierPackageFALLBACK, expectedResult",
|
|
187
|
+
[
|
|
188
|
+
("fibonacci", "hunterMakesPy"), # Should read from pyproject.toml
|
|
189
|
+
("prime", "hunterMakesPy"), # Should read from pyproject.toml
|
|
190
|
+
("nonExistentPackage", "hunterMakesPy"), # Should read from pyproject.toml
|
|
191
|
+
]
|
|
192
|
+
)
|
|
193
|
+
def testGetIdentifierPackagePACKAGING(identifierPackageFALLBACK: str, expectedResult: str) -> None:
|
|
194
|
+
"""Test that getIdentifierPackagePACKAGING reads from pyproject.toml correctly."""
|
|
195
|
+
actual = getIdentifierPackagePACKAGING(identifierPackageFALLBACK)
|
|
196
|
+
assert actual == expectedResult, uniformTestFailureMessage(
|
|
197
|
+
expectedResult, actual, "getIdentifierPackagePACKAGING", identifierPackageFALLBACK
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
@pytest.mark.parametrize(
|
|
201
|
+
"identifierPackage",
|
|
202
|
+
[
|
|
203
|
+
"hunterMakesPy", # This package exists
|
|
204
|
+
"fibonacci", # Non-existent package should fallback to cwd
|
|
205
|
+
"prime", # Non-existent package should fallback to cwd
|
|
206
|
+
]
|
|
207
|
+
)
|
|
208
|
+
def testGetPathPackageINSTALLING(identifierPackage: str) -> None:
|
|
209
|
+
"""Test that getPathPackageINSTALLING returns valid Path objects."""
|
|
210
|
+
actual = getPathPackageINSTALLING(identifierPackage)
|
|
211
|
+
assert isinstance(actual, Path), uniformTestFailureMessage(
|
|
212
|
+
Path, type(actual), "getPathPackageINSTALLING type", identifierPackage
|
|
213
|
+
)
|
|
214
|
+
assert actual.exists() or actual == Path.cwd(), uniformTestFailureMessage(
|
|
215
|
+
"existing path or cwd", actual, "getPathPackageINSTALLING existence", identifierPackage
|
|
216
|
+
)
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"""Primary: settings for this package.
|
|
2
|
-
|
|
3
|
-
Secondary: settings for manufacturing.
|
|
4
|
-
Tertiary: hardcoded values until I implement a dynamic solution.
|
|
5
|
-
"""
|
|
6
|
-
from importlib.util import find_spec
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from tomli import loads as tomli_loads
|
|
9
|
-
from typing import TYPE_CHECKING
|
|
10
|
-
import dataclasses
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from importlib.machinery import ModuleSpec
|
|
14
|
-
|
|
15
|
-
try:
|
|
16
|
-
identifierPackagePACKAGING: str = tomli_loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]["name"]
|
|
17
|
-
except Exception: # noqa: BLE001
|
|
18
|
-
identifierPackagePACKAGING = "hunterMakesPy"
|
|
19
|
-
|
|
20
|
-
def getPathPackageINSTALLING() -> Path:
|
|
21
|
-
"""Return the root directory of the installed package."""
|
|
22
|
-
try:
|
|
23
|
-
moduleSpecification: ModuleSpec | None = find_spec(identifierPackagePACKAGING)
|
|
24
|
-
if moduleSpecification and moduleSpecification.origin:
|
|
25
|
-
pathFilename = Path(moduleSpecification.origin)
|
|
26
|
-
return pathFilename.parent if pathFilename.is_file() else pathFilename
|
|
27
|
-
except ModuleNotFoundError:
|
|
28
|
-
pass
|
|
29
|
-
return Path.cwd()
|
|
30
|
-
|
|
31
|
-
@dataclasses.dataclass
|
|
32
|
-
class PackageSettings:
|
|
33
|
-
fileExtension: str = dataclasses.field(default='.py', metadata={'evaluateWhen': 'installing'})
|
|
34
|
-
"""Default file extension for generated code files."""
|
|
35
|
-
identifierPackage: str = dataclasses.field(default = identifierPackagePACKAGING, metadata={'evaluateWhen': 'packaging'})
|
|
36
|
-
"""Name of this package, used for import paths and configuration."""
|
|
37
|
-
pathPackage: Path = dataclasses.field(default_factory=getPathPackageINSTALLING, metadata={'evaluateWhen': 'installing'})
|
|
38
|
-
"""Absolute path to the installed package directory."""
|
|
39
|
-
|
|
40
|
-
settingsPackage = PackageSettings()
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"""Utility functions for handling `None` values and coping with common programming patterns.
|
|
2
|
-
|
|
3
|
-
(AI generated docstring)
|
|
4
|
-
|
|
5
|
-
This module provides helper functions for defensive programming and error handling, particularly for dealing with `None` values that should not occur in correct program flow.
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
from typing import TypeVar
|
|
9
|
-
|
|
10
|
-
TypeSansNone = TypeVar('TypeSansNone')
|
|
11
|
-
|
|
12
|
-
def raiseIfNone(returnTarget: TypeSansNone | None, errorMessage: str | None = None) -> TypeSansNone:
|
|
13
|
-
"""Raise a `ValueError` if the target value is `None`, otherwise return the value: tell the type checker that the return value is not `None`.
|
|
14
|
-
|
|
15
|
-
(AI generated docstring)
|
|
16
|
-
|
|
17
|
-
This is a defensive programming function that converts unexpected `None` values into explicit errors with context. It is useful for asserting that functions that might return `None` have actually returned a meaningful value.
|
|
18
|
-
|
|
19
|
-
Parameters
|
|
20
|
-
----------
|
|
21
|
-
returnTarget : TypeSansNone | None
|
|
22
|
-
The value to check for `None`. If not `None`, this value is returned unchanged.
|
|
23
|
-
errorMessage : str | None = None
|
|
24
|
-
Custom error message to include in the `ValueError`. If `None`, a default message with debugging hints is used.
|
|
25
|
-
|
|
26
|
-
Returns
|
|
27
|
-
-------
|
|
28
|
-
returnTarget : TypeSansNone
|
|
29
|
-
The original `returnTarget` value, guaranteed to not be `None`.
|
|
30
|
-
|
|
31
|
-
Raises
|
|
32
|
-
------
|
|
33
|
-
ValueError
|
|
34
|
-
If `returnTarget` is `None`.
|
|
35
|
-
|
|
36
|
-
Examples
|
|
37
|
-
--------
|
|
38
|
-
Ensure a function result is not `None`:
|
|
39
|
-
|
|
40
|
-
```python
|
|
41
|
-
def findFirstMatch(listItems: list[str], pattern: str) -> str | None:
|
|
42
|
-
for item in listItems:
|
|
43
|
-
if pattern in item:
|
|
44
|
-
return item
|
|
45
|
-
return None
|
|
46
|
-
|
|
47
|
-
listFiles = ['document.txt', 'image.png', 'data.csv']
|
|
48
|
-
filename = raiseIfNone(findFirstMatch(listFiles, '.txt'))
|
|
49
|
-
# Returns 'document.txt'
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Handle dictionary lookups with custom error messages:
|
|
53
|
-
|
|
54
|
-
```python
|
|
55
|
-
configurationMapping = {'host': 'localhost', 'port': 8080}
|
|
56
|
-
host = raiseIfNone(configurationMapping.get('host'),
|
|
57
|
-
"Configuration must include 'host' setting")
|
|
58
|
-
# Returns 'localhost'
|
|
59
|
-
|
|
60
|
-
# This would raise ValueError with custom message:
|
|
61
|
-
# database = raiseIfNone(configurationMapping.get('database'),
|
|
62
|
-
# "Configuration must include 'database' setting")
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Thanks
|
|
66
|
-
------
|
|
67
|
-
sobolevn, https://github.com/sobolevn, for the seed of the function. https://github.com/python/typing/discussions/1997#discussioncomment-13108399
|
|
68
|
-
|
|
69
|
-
"""
|
|
70
|
-
if returnTarget is None:
|
|
71
|
-
message = errorMessage or 'A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`.'
|
|
72
|
-
raise ValueError(message)
|
|
73
|
-
return returnTarget
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
from hunterMakesPy import raiseIfNone
|
|
2
|
-
from tests.conftest import uniformTestFailureMessage
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
@pytest.mark.parametrize(
|
|
6
|
-
"returnTarget, expected",
|
|
7
|
-
[
|
|
8
|
-
(13, 13),
|
|
9
|
-
(17, 17),
|
|
10
|
-
("fibonacci", "fibonacci"),
|
|
11
|
-
("prime", "prime"),
|
|
12
|
-
([], []),
|
|
13
|
-
({}, {}),
|
|
14
|
-
(False, False),
|
|
15
|
-
(0, 0),
|
|
16
|
-
]
|
|
17
|
-
)
|
|
18
|
-
def testRaiseIfNoneReturnsNonNoneValues(returnTarget: object, expected: object) -> None:
|
|
19
|
-
actual = raiseIfNone(returnTarget)
|
|
20
|
-
assert actual == expected, uniformTestFailureMessage(expected, actual, "testRaiseIfNoneReturnsNonNoneValues", returnTarget)
|
|
21
|
-
assert actual is returnTarget, uniformTestFailureMessage(returnTarget, actual, "testRaiseIfNoneReturnsNonNoneValues identity check", returnTarget)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def testRaiseIfNoneRaisesValueErrorWhenGivenNone() -> None:
|
|
25
|
-
with pytest.raises(ValueError, match="A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`."):
|
|
26
|
-
raiseIfNone(None)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.mark.parametrize(
|
|
30
|
-
"customMessage",
|
|
31
|
-
[
|
|
32
|
-
"Configuration must include 'host' setting",
|
|
33
|
-
"Database connection failed",
|
|
34
|
-
"User input is required",
|
|
35
|
-
"Network request returned empty response",
|
|
36
|
-
]
|
|
37
|
-
)
|
|
38
|
-
def testRaiseIfNoneRaisesValueErrorWithCustomMessage(customMessage: str) -> None:
|
|
39
|
-
with pytest.raises(ValueError, match=customMessage):
|
|
40
|
-
raiseIfNone(None, customMessage)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def testRaiseIfNoneWithEmptyStringMessage() -> None:
|
|
44
|
-
with pytest.raises(ValueError, match="A function unexpectedly returned `None`. Hint: look at the traceback immediately before `raiseIfNone`."):
|
|
45
|
-
raiseIfNone(None, "")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def testRaiseIfNonePreservesTypeAnnotations() -> None:
|
|
49
|
-
integerValue: int = raiseIfNone(23)
|
|
50
|
-
assert isinstance(integerValue, int), uniformTestFailureMessage(int, type(integerValue), "testRaiseIfNonePreservesTypeAnnotations", integerValue)
|
|
51
|
-
|
|
52
|
-
stringValue: str = raiseIfNone("cardinal")
|
|
53
|
-
assert isinstance(stringValue, str), uniformTestFailureMessage(str, type(stringValue), "testRaiseIfNonePreservesTypeAnnotations", stringValue)
|
|
54
|
-
|
|
55
|
-
listValue: list[int] = raiseIfNone([29, 31])
|
|
56
|
-
assert isinstance(listValue, list), uniformTestFailureMessage(list, type(listValue), "testRaiseIfNonePreservesTypeAnnotations", listValue)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|