hunterMakesPy 0.2.0__tar.gz → 0.2.1__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.2.0 → huntermakespy-0.2.1}/PKG-INFO +22 -26
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/README.md +21 -25
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/filesystemToolkit.py +17 -9
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/tests/conftest.py +31 -0
- huntermakespy-0.2.1/hunterMakesPy/tests/test_filesystemToolkit.py +263 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy.egg-info/PKG-INFO +22 -26
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/pyproject.toml +1 -1
- huntermakespy-0.2.0/hunterMakesPy/tests/test_filesystemToolkit.py +0 -46
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/LICENSE +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/__init__.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/_theSSOT.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/coping.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/dataStructures.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/parseParameters.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/py.typed +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/pytestForYourUse.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/tests/__init__.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/tests/test_coping.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/tests/test_dataStructures.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/tests/test_parseParameters.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy/theTypes.py +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy.egg-info/SOURCES.txt +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy.egg-info/dependency_links.txt +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy.egg-info/requires.txt +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/hunterMakesPy.egg-info/top_level.txt +0 -0
- {huntermakespy-0.2.0 → huntermakespy-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hunterMakesPy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.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
|
|
@@ -74,7 +74,7 @@ def findConfiguration(configName: str) -> dict[str, str] | None:
|
|
|
74
74
|
|
|
75
75
|
config = raiseIfNone(
|
|
76
76
|
findConfiguration("database"),
|
|
77
|
-
"Configuration 'database'
|
|
77
|
+
"I could not find Configuration 'database', but I need it to continue."
|
|
78
78
|
)
|
|
79
79
|
```
|
|
80
80
|
|
|
@@ -83,19 +83,19 @@ config = raiseIfNone(
|
|
|
83
83
|
Parameter validation, integer parsing, and concurrency handling.
|
|
84
84
|
|
|
85
85
|
```python
|
|
86
|
-
|
|
86
|
+
import hunterMakesPy as humpy
|
|
87
87
|
|
|
88
88
|
# Smart concurrency limit calculation
|
|
89
|
-
cpuLimit = defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
|
|
90
|
-
cpuLimit = defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
|
|
91
|
-
cpuLimit = defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
|
|
89
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
|
|
90
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
|
|
91
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
|
|
92
92
|
|
|
93
93
|
# Robust integer validation
|
|
94
|
-
validatedIntegers = intInnit([1, "2", 3.0, "4"], "port_numbers")
|
|
94
|
+
validatedIntegers = humpy.intInnit([1, "2", 3.0, "4"], "port_numbers")
|
|
95
95
|
|
|
96
96
|
# String-to-boolean conversion for configuration
|
|
97
97
|
userInput = "True"
|
|
98
|
-
booleanValue = oopsieKwargsie(userInput) # Returns True
|
|
98
|
+
booleanValue = humpy.oopsieKwargsie(userInput) # Returns True
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
## File System Utilities
|
|
@@ -103,20 +103,15 @@ booleanValue = oopsieKwargsie(userInput) # Returns True
|
|
|
103
103
|
Safe file operations and dynamic module importing.
|
|
104
104
|
|
|
105
105
|
```python
|
|
106
|
-
|
|
107
|
-
importLogicalPath2Identifier,
|
|
108
|
-
importPathFilename2Identifier,
|
|
109
|
-
makeDirsSafely,
|
|
110
|
-
writeStringToHere
|
|
111
|
-
)
|
|
106
|
+
import hunterMakesPy as humpy
|
|
112
107
|
|
|
113
108
|
# Dynamic imports
|
|
114
|
-
gcdFunction = importLogicalPath2Identifier("math", "gcd")
|
|
115
|
-
customFunction = importPathFilename2Identifier("path/to/module.py", "functionName")
|
|
109
|
+
gcdFunction = humpy.importLogicalPath2Identifier("math", "gcd")
|
|
110
|
+
customFunction = humpy.importPathFilename2Identifier("path/to/module.py", "functionName")
|
|
116
111
|
|
|
117
112
|
# Safe file operations
|
|
118
113
|
pathFilename = Path("deep/nested/directory/file.txt")
|
|
119
|
-
writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
114
|
+
humpy.writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
120
115
|
```
|
|
121
116
|
|
|
122
117
|
## Data Structure Manipulation
|
|
@@ -124,21 +119,21 @@ writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
|
124
119
|
Utilities for string extraction, data flattening, and array compression.
|
|
125
120
|
|
|
126
121
|
```python
|
|
127
|
-
|
|
122
|
+
import hunterMakesPy as humpy
|
|
128
123
|
import numpy
|
|
129
124
|
|
|
130
125
|
# Extract all strings from nested data structures
|
|
131
126
|
nestedData = {"config": [1, "host", {"port": 8080}], "users": ["alice", "bob"]}
|
|
132
|
-
allStrings = stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
|
|
127
|
+
allStrings = humpy.stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
|
|
133
128
|
|
|
134
129
|
# Merge dictionaries containing lists
|
|
135
|
-
dictionaryAlpha = {"servers": ["
|
|
136
|
-
dictionaryBeta = {"servers": ["
|
|
137
|
-
merged = updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
|
|
130
|
+
dictionaryAlpha = {"servers": ["chicago", "tokyo"], "databases": ["elm"]}
|
|
131
|
+
dictionaryBeta = {"servers": ["mumbai"], "databases": ["oak", "cedar"]}
|
|
132
|
+
merged = humpy.updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
|
|
138
133
|
|
|
139
134
|
# Compress NumPy arrays with run-length encoding
|
|
140
135
|
arrayData = numpy.array([1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9])
|
|
141
|
-
compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
|
|
136
|
+
compressed = humpy.autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
|
|
142
137
|
```
|
|
143
138
|
|
|
144
139
|
## Testing
|
|
@@ -146,7 +141,7 @@ compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]
|
|
|
146
141
|
The package includes comprehensive test suites that you can import and run:
|
|
147
142
|
|
|
148
143
|
```python
|
|
149
|
-
from hunterMakesPy.
|
|
144
|
+
from hunterMakesPy.tests.test_parseParameters import (
|
|
150
145
|
PytestFor_defineConcurrencyLimit,
|
|
151
146
|
PytestFor_intInnit,
|
|
152
147
|
PytestFor_oopsieKwargsie
|
|
@@ -158,8 +153,9 @@ for nameOfTest, callablePytest in listOfTests:
|
|
|
158
153
|
callablePytest()
|
|
159
154
|
|
|
160
155
|
# Or test your own compatible functions
|
|
161
|
-
@pytest.mark.parametrize(
|
|
162
|
-
|
|
156
|
+
@pytest.mark.parametrize(
|
|
157
|
+
"nameOfTest,callablePytest"
|
|
158
|
+
, PytestFor_intInnit(callableToTest=myFunction))
|
|
163
159
|
def test_myFunction(nameOfTest, callablePytest):
|
|
164
160
|
callablePytest()
|
|
165
161
|
```
|
|
@@ -28,7 +28,7 @@ def findConfiguration(configName: str) -> dict[str, str] | None:
|
|
|
28
28
|
|
|
29
29
|
config = raiseIfNone(
|
|
30
30
|
findConfiguration("database"),
|
|
31
|
-
"Configuration 'database'
|
|
31
|
+
"I could not find Configuration 'database', but I need it to continue."
|
|
32
32
|
)
|
|
33
33
|
```
|
|
34
34
|
|
|
@@ -37,19 +37,19 @@ config = raiseIfNone(
|
|
|
37
37
|
Parameter validation, integer parsing, and concurrency handling.
|
|
38
38
|
|
|
39
39
|
```python
|
|
40
|
-
|
|
40
|
+
import hunterMakesPy as humpy
|
|
41
41
|
|
|
42
42
|
# Smart concurrency limit calculation
|
|
43
|
-
cpuLimit = defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
|
|
44
|
-
cpuLimit = defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
|
|
45
|
-
cpuLimit = defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
|
|
43
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
|
|
44
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
|
|
45
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
|
|
46
46
|
|
|
47
47
|
# Robust integer validation
|
|
48
|
-
validatedIntegers = intInnit([1, "2", 3.0, "4"], "port_numbers")
|
|
48
|
+
validatedIntegers = humpy.intInnit([1, "2", 3.0, "4"], "port_numbers")
|
|
49
49
|
|
|
50
50
|
# String-to-boolean conversion for configuration
|
|
51
51
|
userInput = "True"
|
|
52
|
-
booleanValue = oopsieKwargsie(userInput) # Returns True
|
|
52
|
+
booleanValue = humpy.oopsieKwargsie(userInput) # Returns True
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
## File System Utilities
|
|
@@ -57,20 +57,15 @@ booleanValue = oopsieKwargsie(userInput) # Returns True
|
|
|
57
57
|
Safe file operations and dynamic module importing.
|
|
58
58
|
|
|
59
59
|
```python
|
|
60
|
-
|
|
61
|
-
importLogicalPath2Identifier,
|
|
62
|
-
importPathFilename2Identifier,
|
|
63
|
-
makeDirsSafely,
|
|
64
|
-
writeStringToHere
|
|
65
|
-
)
|
|
60
|
+
import hunterMakesPy as humpy
|
|
66
61
|
|
|
67
62
|
# Dynamic imports
|
|
68
|
-
gcdFunction = importLogicalPath2Identifier("math", "gcd")
|
|
69
|
-
customFunction = importPathFilename2Identifier("path/to/module.py", "functionName")
|
|
63
|
+
gcdFunction = humpy.importLogicalPath2Identifier("math", "gcd")
|
|
64
|
+
customFunction = humpy.importPathFilename2Identifier("path/to/module.py", "functionName")
|
|
70
65
|
|
|
71
66
|
# Safe file operations
|
|
72
67
|
pathFilename = Path("deep/nested/directory/file.txt")
|
|
73
|
-
writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
68
|
+
humpy.writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
74
69
|
```
|
|
75
70
|
|
|
76
71
|
## Data Structure Manipulation
|
|
@@ -78,21 +73,21 @@ writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
|
78
73
|
Utilities for string extraction, data flattening, and array compression.
|
|
79
74
|
|
|
80
75
|
```python
|
|
81
|
-
|
|
76
|
+
import hunterMakesPy as humpy
|
|
82
77
|
import numpy
|
|
83
78
|
|
|
84
79
|
# Extract all strings from nested data structures
|
|
85
80
|
nestedData = {"config": [1, "host", {"port": 8080}], "users": ["alice", "bob"]}
|
|
86
|
-
allStrings = stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
|
|
81
|
+
allStrings = humpy.stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
|
|
87
82
|
|
|
88
83
|
# Merge dictionaries containing lists
|
|
89
|
-
dictionaryAlpha = {"servers": ["
|
|
90
|
-
dictionaryBeta = {"servers": ["
|
|
91
|
-
merged = updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
|
|
84
|
+
dictionaryAlpha = {"servers": ["chicago", "tokyo"], "databases": ["elm"]}
|
|
85
|
+
dictionaryBeta = {"servers": ["mumbai"], "databases": ["oak", "cedar"]}
|
|
86
|
+
merged = humpy.updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
|
|
92
87
|
|
|
93
88
|
# Compress NumPy arrays with run-length encoding
|
|
94
89
|
arrayData = numpy.array([1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9])
|
|
95
|
-
compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
|
|
90
|
+
compressed = humpy.autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
|
|
96
91
|
```
|
|
97
92
|
|
|
98
93
|
## Testing
|
|
@@ -100,7 +95,7 @@ compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]
|
|
|
100
95
|
The package includes comprehensive test suites that you can import and run:
|
|
101
96
|
|
|
102
97
|
```python
|
|
103
|
-
from hunterMakesPy.
|
|
98
|
+
from hunterMakesPy.tests.test_parseParameters import (
|
|
104
99
|
PytestFor_defineConcurrencyLimit,
|
|
105
100
|
PytestFor_intInnit,
|
|
106
101
|
PytestFor_oopsieKwargsie
|
|
@@ -112,8 +107,9 @@ for nameOfTest, callablePytest in listOfTests:
|
|
|
112
107
|
callablePytest()
|
|
113
108
|
|
|
114
109
|
# Or test your own compatible functions
|
|
115
|
-
@pytest.mark.parametrize(
|
|
116
|
-
|
|
110
|
+
@pytest.mark.parametrize(
|
|
111
|
+
"nameOfTest,callablePytest"
|
|
112
|
+
, PytestFor_intInnit(callableToTest=myFunction))
|
|
117
113
|
def test_myFunction(nameOfTest, callablePytest):
|
|
118
114
|
callablePytest()
|
|
119
115
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""File system and module import utilities.
|
|
2
2
|
|
|
3
|
-
This module provides basic file I/O utilities such as importing callables from modules,
|
|
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
|
|
|
@@ -97,17 +97,25 @@ def makeDirsSafely(pathFilename: Any) -> None:
|
|
|
97
97
|
with contextlib.suppress(OSError):
|
|
98
98
|
Path(pathFilename).parent.mkdir(parents=True, exist_ok=True)
|
|
99
99
|
|
|
100
|
-
def writeStringToHere(this: str, pathFilename: PathLike[Any] | PurePath) -> None:
|
|
101
|
-
"""Write a string to a file
|
|
100
|
+
def writeStringToHere(this: str, pathFilename: PathLike[Any] | PurePath | io.TextIOBase) -> None:
|
|
101
|
+
"""Write a string to a file or text stream.
|
|
102
|
+
|
|
103
|
+
This function writes a string to either a file path or an open text stream. For file paths, it creates parent directories as
|
|
104
|
+
needed and writes with UTF-8 encoding. For text streams, it writes directly to the stream and flushes the buffer.
|
|
102
105
|
|
|
103
106
|
Parameters
|
|
104
107
|
----------
|
|
105
108
|
this : str
|
|
106
|
-
The string content to write
|
|
107
|
-
pathFilename : PathLike[Any] | PurePath
|
|
108
|
-
The
|
|
109
|
+
The string content to write.
|
|
110
|
+
pathFilename : PathLike[Any] | PurePath | io.TextIOBase
|
|
111
|
+
The target destination: either a file path or an open text stream.
|
|
109
112
|
|
|
110
113
|
"""
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
if isinstance(pathFilename, io.TextIOBase):
|
|
115
|
+
pathFilename.write(str(this))
|
|
116
|
+
pathFilename.flush()
|
|
117
|
+
else:
|
|
118
|
+
pathFilename = Path(pathFilename)
|
|
119
|
+
makeDirsSafely(pathFilename)
|
|
120
|
+
pathFilename.write_text(str(this), encoding='utf-8')
|
|
121
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# pyright: standard
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from typing import Any
|
|
4
|
+
import io
|
|
4
5
|
import pathlib
|
|
5
6
|
import pytest
|
|
6
7
|
|
|
@@ -12,6 +13,36 @@ pathDataSamples = pathlib.Path("hunterMakesPy/tests/dataSamples")
|
|
|
12
13
|
def pathTmpTesting(tmp_path: pathlib.Path) -> pathlib.Path:
|
|
13
14
|
return tmp_path
|
|
14
15
|
|
|
16
|
+
# Fixture for predictable Python source code samples
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def dictionaryPythonSourceSamples() -> dict[str, str]:
|
|
19
|
+
"""Provide predictable Python source code samples for testing."""
|
|
20
|
+
return {
|
|
21
|
+
'functionFibonacci': "def fibonacciNumber():\n return 13\n",
|
|
22
|
+
'functionPrime': "def primeNumber():\n return 17\n",
|
|
23
|
+
'variablePrime': "prime = 19\n",
|
|
24
|
+
'variableFibonacci': "fibonacci = 21\n",
|
|
25
|
+
'classCardinal': "class CardinalDirection:\n north = 'N'\n south = 'S'\n",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Fixture for IO stream objects
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def streamMemoryString() -> io.StringIO:
|
|
31
|
+
"""Provide a StringIO object for testing stream operations."""
|
|
32
|
+
return io.StringIO()
|
|
33
|
+
|
|
34
|
+
# Fixture for predictable directory names using cardinal directions
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def listDirectoryNamesCardinal() -> list[str]:
|
|
37
|
+
"""Provide predictable directory names using cardinal directions."""
|
|
38
|
+
return ['north', 'south', 'east', 'west']
|
|
39
|
+
|
|
40
|
+
# Fixture for predictable file content using Fibonacci numbers
|
|
41
|
+
@pytest.fixture
|
|
42
|
+
def listFileContentsFibonacci() -> list[str]:
|
|
43
|
+
"""Provide predictable file contents using Fibonacci sequence."""
|
|
44
|
+
return ['fibonacci8', 'fibonacci13', 'fibonacci21', 'fibonacci34']
|
|
45
|
+
|
|
15
46
|
def uniformTestFailureMessage(expected: Any, actual: Any, functionName: str, *arguments: Any, **keywordArguments: Any) -> str:
|
|
16
47
|
"""Format assertion message for any test comparison."""
|
|
17
48
|
listArgumentComponents: list[str] = [str(parameter) for parameter in arguments]
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# pyright: standard
|
|
2
|
+
from hunterMakesPy import importLogicalPath2Identifier, importPathFilename2Identifier, makeDirsSafely, writeStringToHere
|
|
3
|
+
from hunterMakesPy.tests.conftest import standardizedEqualTo
|
|
4
|
+
import io
|
|
5
|
+
import math
|
|
6
|
+
import os
|
|
7
|
+
import pathlib
|
|
8
|
+
import pytest
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
@pytest.mark.parametrize(
|
|
12
|
+
"logicalPathModuleTarget, identifierTarget, packageIdentifierIfRelativeTarget, expectedType",
|
|
13
|
+
[
|
|
14
|
+
('math', 'gcd', None, type(math.gcd)),
|
|
15
|
+
('os.path', 'join', None, type(os.path.join)),
|
|
16
|
+
('pathlib', 'Path', None, type(pathlib.Path)),
|
|
17
|
+
('sys', 'version', None, type(sys.version)),
|
|
18
|
+
]
|
|
19
|
+
)
|
|
20
|
+
def testImportLogicalPath2IdentifierWithAbsolutePaths(
|
|
21
|
+
logicalPathModuleTarget: str,
|
|
22
|
+
identifierTarget: str,
|
|
23
|
+
packageIdentifierIfRelativeTarget: str | None,
|
|
24
|
+
expectedType: type
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Test importing identifiers from modules using absolute logical paths."""
|
|
27
|
+
identifierImported = importLogicalPath2Identifier(logicalPathModuleTarget, identifierTarget, packageIdentifierIfRelativeTarget)
|
|
28
|
+
|
|
29
|
+
assert isinstance(identifierImported, expectedType), (
|
|
30
|
+
f"\nTesting: `importLogicalPath2Identifier({logicalPathModuleTarget}, {identifierTarget}, {packageIdentifierIfRelativeTarget})`\n"
|
|
31
|
+
f"Expected type: {expectedType}\n"
|
|
32
|
+
f"Got type: {type(identifierImported)}"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize(
|
|
37
|
+
"pythonSourceTarget, identifierTarget, moduleIdentifierTarget, expectedValueWhenCalled",
|
|
38
|
+
[
|
|
39
|
+
("def fibonacciNumber():\n return 13\n", "fibonacciNumber", None, 13),
|
|
40
|
+
("def primeNumber():\n return 17\n", "primeNumber", "moduleNorth", 17),
|
|
41
|
+
("def cardinalDirection():\n return 'N'\n", "cardinalDirection", "moduleSouth", 'N'),
|
|
42
|
+
("def fibonacciSequence():\n return 21\n", "fibonacciSequence", "moduleEast", 21),
|
|
43
|
+
]
|
|
44
|
+
)
|
|
45
|
+
def testImportPathFilename2IdentifierWithCallables(
|
|
46
|
+
pathTmpTesting: pathlib.Path,
|
|
47
|
+
pythonSourceTarget: str,
|
|
48
|
+
identifierTarget: str,
|
|
49
|
+
moduleIdentifierTarget: str | None,
|
|
50
|
+
expectedValueWhenCalled: object
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Test importing callable identifiers from Python files."""
|
|
53
|
+
pathFilenameModule = pathTmpTesting / f"moduleTest{hash(pythonSourceTarget) % 89}.py" # Use prime number 89
|
|
54
|
+
pathFilenameModule.write_text(pythonSourceTarget)
|
|
55
|
+
|
|
56
|
+
standardizedEqualTo(
|
|
57
|
+
expectedValueWhenCalled,
|
|
58
|
+
lambda: importPathFilename2Identifier(pathFilenameModule, identifierTarget, moduleIdentifierTarget)(),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@pytest.mark.parametrize(
|
|
63
|
+
"pythonSourceTarget, identifierTarget, moduleIdentifierTarget, expectedValue",
|
|
64
|
+
[
|
|
65
|
+
("prime = 23\n", "prime", None, 23),
|
|
66
|
+
("fibonacci = 34\n", "fibonacci", "moduleWest", 34),
|
|
67
|
+
("cardinalDirection = 'S'\n", "cardinalDirection", "moduleNorthEast", 'S'),
|
|
68
|
+
("sequenceValue = 55\n", "sequenceValue", "moduleSouthWest", 55),
|
|
69
|
+
]
|
|
70
|
+
)
|
|
71
|
+
def testImportPathFilename2IdentifierWithVariables(
|
|
72
|
+
pathTmpTesting: pathlib.Path,
|
|
73
|
+
pythonSourceTarget: str,
|
|
74
|
+
identifierTarget: str,
|
|
75
|
+
moduleIdentifierTarget: str | None,
|
|
76
|
+
expectedValue: object
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Test importing variable identifiers from Python files."""
|
|
79
|
+
pathFilenameModule = pathTmpTesting / f"moduleTest{hash(pythonSourceTarget) % 97}.py" # Use prime number 97
|
|
80
|
+
pathFilenameModule.write_text(pythonSourceTarget)
|
|
81
|
+
|
|
82
|
+
standardizedEqualTo(
|
|
83
|
+
expectedValue,
|
|
84
|
+
importPathFilename2Identifier,
|
|
85
|
+
pathFilenameModule,
|
|
86
|
+
identifierTarget,
|
|
87
|
+
moduleIdentifierTarget
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize(
|
|
92
|
+
"listDirectoryComponents, filenameTarget",
|
|
93
|
+
[
|
|
94
|
+
(['north', 'south'], 'fibonacci13.txt'),
|
|
95
|
+
(['east', 'west', 'northeast'], 'prime17.txt'),
|
|
96
|
+
(['southwest', 'northwest'], 'fibonacci21.txt'),
|
|
97
|
+
(['cardinal', 'directions', 'multiple'], 'prime23.txt'),
|
|
98
|
+
]
|
|
99
|
+
)
|
|
100
|
+
def testMakeDirsSafelyCreatesNestedDirectories(
|
|
101
|
+
pathTmpTesting: pathlib.Path,
|
|
102
|
+
listDirectoryComponents: list[str],
|
|
103
|
+
filenameTarget: str
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Test that makeDirsSafely creates nested parent directories."""
|
|
106
|
+
pathDirectoryNested = pathTmpTesting
|
|
107
|
+
for directoryComponent in listDirectoryComponents:
|
|
108
|
+
pathDirectoryNested = pathDirectoryNested / directoryComponent
|
|
109
|
+
|
|
110
|
+
pathFilenameTarget = pathDirectoryNested / filenameTarget
|
|
111
|
+
makeDirsSafely(pathFilenameTarget)
|
|
112
|
+
|
|
113
|
+
assert pathDirectoryNested.exists() and pathDirectoryNested.is_dir(), (
|
|
114
|
+
f"\nTesting: `makeDirsSafely({pathFilenameTarget})`\n"
|
|
115
|
+
f"Expected: Directory {pathDirectoryNested} to exist and be a directory\n"
|
|
116
|
+
f"Got: exists={pathDirectoryNested.exists()}, is_dir={pathDirectoryNested.is_dir() if pathDirectoryNested.exists() else False}"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@pytest.mark.parametrize(
|
|
121
|
+
"streamTypeTarget",
|
|
122
|
+
[
|
|
123
|
+
io.StringIO(),
|
|
124
|
+
io.StringIO("initialContent"),
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
def testMakeDirsSafelyWithIOStreamDoesNotRaise(streamTypeTarget: io.IOBase) -> None:
|
|
128
|
+
"""Test that makeDirsSafely handles IO streams without raising exceptions."""
|
|
129
|
+
# This test verifies that no exception is raised
|
|
130
|
+
makeDirsSafely(streamTypeTarget)
|
|
131
|
+
|
|
132
|
+
# If we reach this point, no exception was raised
|
|
133
|
+
assert True
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.parametrize(
|
|
137
|
+
"listDirectoryComponents, filenameTarget, contentTarget",
|
|
138
|
+
[
|
|
139
|
+
(['north', 'fibonacci'], 'test13.txt', 'fibonacci content 13'),
|
|
140
|
+
(['south', 'prime'], 'test17.txt', 'prime content 17'),
|
|
141
|
+
(['east', 'cardinal'], 'test21.txt', 'cardinal direction east'),
|
|
142
|
+
(['west', 'sequence'], 'test23.txt', 'sequence value 23'),
|
|
143
|
+
]
|
|
144
|
+
)
|
|
145
|
+
def testWriteStringToHereCreatesFileAndDirectories(
|
|
146
|
+
pathTmpTesting: pathlib.Path,
|
|
147
|
+
listDirectoryComponents: list[str],
|
|
148
|
+
filenameTarget: str,
|
|
149
|
+
contentTarget: str
|
|
150
|
+
) -> None:
|
|
151
|
+
"""Test that writeStringToHere creates directories and writes content to files."""
|
|
152
|
+
pathDirectoryNested = pathTmpTesting
|
|
153
|
+
for directoryComponent in listDirectoryComponents:
|
|
154
|
+
pathDirectoryNested = pathDirectoryNested / directoryComponent
|
|
155
|
+
|
|
156
|
+
pathFilenameTarget = pathDirectoryNested / filenameTarget
|
|
157
|
+
writeStringToHere(contentTarget, pathFilenameTarget)
|
|
158
|
+
|
|
159
|
+
assert pathFilenameTarget.exists(), (
|
|
160
|
+
f"\nTesting: `writeStringToHere({contentTarget}, {pathFilenameTarget})`\n"
|
|
161
|
+
f"Expected: File {pathFilenameTarget} to exist\n"
|
|
162
|
+
f"Got: exists={pathFilenameTarget.exists()}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
contentActual = pathFilenameTarget.read_text(encoding="utf-8")
|
|
166
|
+
assert contentActual == contentTarget, (
|
|
167
|
+
f"\nTesting: `writeStringToHere({contentTarget}, {pathFilenameTarget})`\n"
|
|
168
|
+
f"Expected content: {contentTarget}\n"
|
|
169
|
+
f"Got content: {contentActual}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@pytest.mark.parametrize(
|
|
174
|
+
"contentTarget",
|
|
175
|
+
[
|
|
176
|
+
'fibonacci content 34',
|
|
177
|
+
'prime content 29',
|
|
178
|
+
'cardinal direction NE',
|
|
179
|
+
'sequence value 55',
|
|
180
|
+
]
|
|
181
|
+
)
|
|
182
|
+
def testWriteStringToHereWithIOStream(contentTarget: str) -> None:
|
|
183
|
+
"""Test that writeStringToHere writes content to IO streams."""
|
|
184
|
+
streamMemory = io.StringIO()
|
|
185
|
+
writeStringToHere(contentTarget, streamMemory)
|
|
186
|
+
|
|
187
|
+
contentActual = streamMemory.getvalue()
|
|
188
|
+
assert contentActual == contentTarget, (
|
|
189
|
+
f"\nTesting: `writeStringToHere({contentTarget}, StringIO)`\n"
|
|
190
|
+
f"Expected content: {contentTarget}\n"
|
|
191
|
+
f"Got content: {contentActual}"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@pytest.mark.parametrize(
|
|
196
|
+
"logicalPathModuleTarget, identifierTarget, expectedExceptionType",
|
|
197
|
+
[
|
|
198
|
+
('nonexistent.module', 'anyIdentifier', ModuleNotFoundError),
|
|
199
|
+
('math', 'nonexistentFunction', AttributeError),
|
|
200
|
+
('os.path', 'nonexistentAttribute', AttributeError),
|
|
201
|
+
]
|
|
202
|
+
)
|
|
203
|
+
def testImportLogicalPath2IdentifierWithInvalidInputs(
|
|
204
|
+
logicalPathModuleTarget: str,
|
|
205
|
+
identifierTarget: str,
|
|
206
|
+
expectedExceptionType: type[Exception]
|
|
207
|
+
) -> None:
|
|
208
|
+
"""Test that importLogicalPath2Identifier raises appropriate exceptions for invalid inputs."""
|
|
209
|
+
standardizedEqualTo(
|
|
210
|
+
expectedExceptionType,
|
|
211
|
+
importLogicalPath2Identifier,
|
|
212
|
+
logicalPathModuleTarget,
|
|
213
|
+
identifierTarget
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@pytest.mark.parametrize(
|
|
218
|
+
"pathFilenameTarget, identifierTarget, expectedExceptionType",
|
|
219
|
+
[
|
|
220
|
+
('nonexistent.py', 'anyIdentifier', FileNotFoundError),
|
|
221
|
+
]
|
|
222
|
+
)
|
|
223
|
+
def testImportPathFilename2IdentifierWithInvalidInputs(
|
|
224
|
+
pathTmpTesting: pathlib.Path,
|
|
225
|
+
pathFilenameTarget: str,
|
|
226
|
+
identifierTarget: str,
|
|
227
|
+
expectedExceptionType: type[Exception]
|
|
228
|
+
) -> None:
|
|
229
|
+
"""Test that importPathFilename2Identifier raises appropriate exceptions for invalid inputs."""
|
|
230
|
+
pathFilenameNonexistent = pathTmpTesting / pathFilenameTarget
|
|
231
|
+
|
|
232
|
+
standardizedEqualTo(
|
|
233
|
+
expectedExceptionType,
|
|
234
|
+
importPathFilename2Identifier,
|
|
235
|
+
pathFilenameNonexistent,
|
|
236
|
+
identifierTarget
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@pytest.mark.parametrize(
|
|
241
|
+
"pythonSourceTarget, identifierTarget, expectedExceptionType",
|
|
242
|
+
[
|
|
243
|
+
("def validFunction():\n return 89\n", "nonexistentIdentifier", AttributeError),
|
|
244
|
+
("validVariable = 97\n", "nonexistentVariable", AttributeError),
|
|
245
|
+
]
|
|
246
|
+
)
|
|
247
|
+
def testImportPathFilename2IdentifierWithValidFileInvalidIdentifier(
|
|
248
|
+
pathTmpTesting: pathlib.Path,
|
|
249
|
+
pythonSourceTarget: str,
|
|
250
|
+
identifierTarget: str,
|
|
251
|
+
expectedExceptionType: type[Exception]
|
|
252
|
+
) -> None:
|
|
253
|
+
"""Test that importPathFilename2Identifier raises AttributeError for nonexistent identifiers in valid files."""
|
|
254
|
+
pathFilenameModule = pathTmpTesting / f"moduleTest{hash(pythonSourceTarget) % 101}.py" # Use prime number 101
|
|
255
|
+
pathFilenameModule.write_text(pythonSourceTarget)
|
|
256
|
+
|
|
257
|
+
standardizedEqualTo(
|
|
258
|
+
expectedExceptionType,
|
|
259
|
+
importPathFilename2Identifier,
|
|
260
|
+
pathFilenameModule,
|
|
261
|
+
identifierTarget
|
|
262
|
+
)
|
|
263
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hunterMakesPy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.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
|
|
@@ -74,7 +74,7 @@ def findConfiguration(configName: str) -> dict[str, str] | None:
|
|
|
74
74
|
|
|
75
75
|
config = raiseIfNone(
|
|
76
76
|
findConfiguration("database"),
|
|
77
|
-
"Configuration 'database'
|
|
77
|
+
"I could not find Configuration 'database', but I need it to continue."
|
|
78
78
|
)
|
|
79
79
|
```
|
|
80
80
|
|
|
@@ -83,19 +83,19 @@ config = raiseIfNone(
|
|
|
83
83
|
Parameter validation, integer parsing, and concurrency handling.
|
|
84
84
|
|
|
85
85
|
```python
|
|
86
|
-
|
|
86
|
+
import hunterMakesPy as humpy
|
|
87
87
|
|
|
88
88
|
# Smart concurrency limit calculation
|
|
89
|
-
cpuLimit = defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
|
|
90
|
-
cpuLimit = defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
|
|
91
|
-
cpuLimit = defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
|
|
89
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=0.75) # Use 75% of available CPUs
|
|
90
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=True) # Use exactly 1 CPU
|
|
91
|
+
cpuLimit = humpy.defineConcurrencyLimit(limit=4) # Use exactly 4 CPUs
|
|
92
92
|
|
|
93
93
|
# Robust integer validation
|
|
94
|
-
validatedIntegers = intInnit([1, "2", 3.0, "4"], "port_numbers")
|
|
94
|
+
validatedIntegers = humpy.intInnit([1, "2", 3.0, "4"], "port_numbers")
|
|
95
95
|
|
|
96
96
|
# String-to-boolean conversion for configuration
|
|
97
97
|
userInput = "True"
|
|
98
|
-
booleanValue = oopsieKwargsie(userInput) # Returns True
|
|
98
|
+
booleanValue = humpy.oopsieKwargsie(userInput) # Returns True
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
## File System Utilities
|
|
@@ -103,20 +103,15 @@ booleanValue = oopsieKwargsie(userInput) # Returns True
|
|
|
103
103
|
Safe file operations and dynamic module importing.
|
|
104
104
|
|
|
105
105
|
```python
|
|
106
|
-
|
|
107
|
-
importLogicalPath2Identifier,
|
|
108
|
-
importPathFilename2Identifier,
|
|
109
|
-
makeDirsSafely,
|
|
110
|
-
writeStringToHere
|
|
111
|
-
)
|
|
106
|
+
import hunterMakesPy as humpy
|
|
112
107
|
|
|
113
108
|
# Dynamic imports
|
|
114
|
-
gcdFunction = importLogicalPath2Identifier("math", "gcd")
|
|
115
|
-
customFunction = importPathFilename2Identifier("path/to/module.py", "functionName")
|
|
109
|
+
gcdFunction = humpy.importLogicalPath2Identifier("math", "gcd")
|
|
110
|
+
customFunction = humpy.importPathFilename2Identifier("path/to/module.py", "functionName")
|
|
116
111
|
|
|
117
112
|
# Safe file operations
|
|
118
113
|
pathFilename = Path("deep/nested/directory/file.txt")
|
|
119
|
-
writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
114
|
+
humpy.writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
120
115
|
```
|
|
121
116
|
|
|
122
117
|
## Data Structure Manipulation
|
|
@@ -124,21 +119,21 @@ writeStringToHere("content", pathFilename) # Creates directories automatically
|
|
|
124
119
|
Utilities for string extraction, data flattening, and array compression.
|
|
125
120
|
|
|
126
121
|
```python
|
|
127
|
-
|
|
122
|
+
import hunterMakesPy as humpy
|
|
128
123
|
import numpy
|
|
129
124
|
|
|
130
125
|
# Extract all strings from nested data structures
|
|
131
126
|
nestedData = {"config": [1, "host", {"port": 8080}], "users": ["alice", "bob"]}
|
|
132
|
-
allStrings = stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
|
|
127
|
+
allStrings = humpy.stringItUp(nestedData) # ['config', 'host', 'port', 'users', 'alice', 'bob']
|
|
133
128
|
|
|
134
129
|
# Merge dictionaries containing lists
|
|
135
|
-
dictionaryAlpha = {"servers": ["
|
|
136
|
-
dictionaryBeta = {"servers": ["
|
|
137
|
-
merged = updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
|
|
130
|
+
dictionaryAlpha = {"servers": ["chicago", "tokyo"], "databases": ["elm"]}
|
|
131
|
+
dictionaryBeta = {"servers": ["mumbai"], "databases": ["oak", "cedar"]}
|
|
132
|
+
merged = humpy.updateExtendPolishDictionaryLists(dictionaryAlpha, dictionaryBeta, destroyDuplicates=True)
|
|
138
133
|
|
|
139
134
|
# Compress NumPy arrays with run-length encoding
|
|
140
135
|
arrayData = numpy.array([1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9])
|
|
141
|
-
compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
|
|
136
|
+
compressed = humpy.autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]"
|
|
142
137
|
```
|
|
143
138
|
|
|
144
139
|
## Testing
|
|
@@ -146,7 +141,7 @@ compressed = autoDecodingRLE(arrayData) # "[1,*range(2,6)]+[5]*2+[*range(6,10)]
|
|
|
146
141
|
The package includes comprehensive test suites that you can import and run:
|
|
147
142
|
|
|
148
143
|
```python
|
|
149
|
-
from hunterMakesPy.
|
|
144
|
+
from hunterMakesPy.tests.test_parseParameters import (
|
|
150
145
|
PytestFor_defineConcurrencyLimit,
|
|
151
146
|
PytestFor_intInnit,
|
|
152
147
|
PytestFor_oopsieKwargsie
|
|
@@ -158,8 +153,9 @@ for nameOfTest, callablePytest in listOfTests:
|
|
|
158
153
|
callablePytest()
|
|
159
154
|
|
|
160
155
|
# Or test your own compatible functions
|
|
161
|
-
@pytest.mark.parametrize(
|
|
162
|
-
|
|
156
|
+
@pytest.mark.parametrize(
|
|
157
|
+
"nameOfTest,callablePytest"
|
|
158
|
+
, PytestFor_intInnit(callableToTest=myFunction))
|
|
163
159
|
def test_myFunction(nameOfTest, callablePytest):
|
|
164
160
|
callablePytest()
|
|
165
161
|
```
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# pyright: standard
|
|
2
|
-
from hunterMakesPy import importLogicalPath2Identifier, importPathFilename2Identifier, makeDirsSafely, writeStringToHere
|
|
3
|
-
from hunterMakesPy.tests.conftest import uniformTestFailureMessage
|
|
4
|
-
import io
|
|
5
|
-
import math
|
|
6
|
-
import os
|
|
7
|
-
import pathlib
|
|
8
|
-
import pytest
|
|
9
|
-
|
|
10
|
-
def testMakeDirsSafelyCreatesParentDirectories(pathTmpTesting: pathlib.Path) -> None:
|
|
11
|
-
nestedDirectory = pathTmpTesting / "sub1" / "sub2"
|
|
12
|
-
filePath = nestedDirectory / "dummy.txt"
|
|
13
|
-
makeDirsSafely(filePath)
|
|
14
|
-
assert nestedDirectory.exists() and nestedDirectory.is_dir(), uniformTestFailureMessage(True, nestedDirectory.exists() and nestedDirectory.is_dir(), "testMakeDirsSafelyCreatesParentDirectories", filePath)
|
|
15
|
-
|
|
16
|
-
def testMakeDirsSafelyWithIOBaseDoesNotRaise() -> None:
|
|
17
|
-
memoryStream = io.StringIO()
|
|
18
|
-
makeDirsSafely(memoryStream)
|
|
19
|
-
|
|
20
|
-
def testWriteStringToHereCreatesFileAndWritesContent(pathTmpTesting: pathlib.Path) -> None:
|
|
21
|
-
nestedDirectory = pathTmpTesting / "a" / "b"
|
|
22
|
-
filePath = nestedDirectory / "test.txt"
|
|
23
|
-
writeStringToHere("hello world", filePath)
|
|
24
|
-
assert filePath.exists(), uniformTestFailureMessage(True, filePath.exists(), "testWriteStringToHereCreatesFileAndWritesContent", filePath)
|
|
25
|
-
assert filePath.read_text(encoding="utf-8") == "hello world", uniformTestFailureMessage("hello world", filePath.read_text(encoding="utf-8"), "testWriteStringToHereCreatesFileAndWritesContent", filePath)
|
|
26
|
-
|
|
27
|
-
@pytest.mark.parametrize(
|
|
28
|
-
"moduleName, identifier, expectedType",
|
|
29
|
-
[("math", "gcd", type(math.gcd)),("os.path", "join", type(os.path.join))])
|
|
30
|
-
def testImportLogicalPath2Identifier(moduleName: str, identifier: str, expectedType: type) -> None:
|
|
31
|
-
imported = importLogicalPath2Identifier(moduleName, identifier)
|
|
32
|
-
assert isinstance(imported, expectedType), uniformTestFailureMessage(expectedType, type(imported), "testImportLogicalPath2Identifier", (moduleName, identifier))
|
|
33
|
-
|
|
34
|
-
@pytest.mark.parametrize(
|
|
35
|
-
"source, identifier, expected"
|
|
36
|
-
, [("def fibonacciNumber():\n return 13\n", "fibonacciNumber", 13)
|
|
37
|
-
, ("prime = 17\n", "prime", 17)])
|
|
38
|
-
def testImportPathFilename2Identifier(tmp_path: pathlib.Path, source: str, identifier: str, expected: object) -> None:
|
|
39
|
-
filePath = tmp_path / "moduleTest.py"
|
|
40
|
-
filePath.write_text(source)
|
|
41
|
-
imported = importPathFilename2Identifier(filePath, identifier)
|
|
42
|
-
if callable(imported):
|
|
43
|
-
actual = imported()
|
|
44
|
-
else:
|
|
45
|
-
actual = imported
|
|
46
|
-
assert actual == expected, uniformTestFailureMessage(expected, actual, "testImportPathFilename2Identifier", (filePath, identifier))
|
|
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
|
|
File without changes
|
|
File without changes
|