hunterMakesPy 0.1.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/__init__.py +16 -0
- hunterMakesPy/_theSSOT.py +40 -0
- hunterMakesPy/coping.py +73 -0
- hunterMakesPy/dataStructures.py +265 -0
- hunterMakesPy/filesystemToolkit.py +114 -0
- hunterMakesPy/parseParameters.py +322 -0
- hunterMakesPy/py.typed +0 -0
- hunterMakesPy/pytestForYourUse.py +327 -0
- hunterMakesPy/theTypes.py +9 -0
- huntermakespy-0.1.0.dist-info/METADATA +38 -0
- huntermakespy-0.1.0.dist-info/RECORD +20 -0
- huntermakespy-0.1.0.dist-info/WHEEL +5 -0
- huntermakespy-0.1.0.dist-info/licenses/LICENSE +407 -0
- huntermakespy-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/conftest.py +39 -0
- tests/test_coping.py +56 -0
- tests/test_dataStructures.py +319 -0
- tests/test_filesystemToolkit.py +43 -0
- tests/test_parseParameters.py +21 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# pyright: standard
|
|
2
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from fractions import Fraction
|
|
5
|
+
from hunterMakesPy import autoDecodingRLE, stringItUp, updateExtendPolishDictionaryLists
|
|
6
|
+
from numpy.typing import NDArray
|
|
7
|
+
from tests.conftest import standardizedEqualTo
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
import datetime
|
|
10
|
+
import numpy
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
class CustomIterable:
|
|
14
|
+
def __init__(self, items: Iterable[Any]) -> None: self.items = items
|
|
15
|
+
def __iter__(self) -> Iterator[Any]: return iter(self.items)
|
|
16
|
+
|
|
17
|
+
@pytest.mark.parametrize("description,value_scrapPile,expected", [
|
|
18
|
+
# Basic types and structures
|
|
19
|
+
("Empty input", [], []),
|
|
20
|
+
("Prime numbers", [11, 13, 17], ['11', '13', '17']),
|
|
21
|
+
("Cardinal directions", ["NE", "SW", "SE"], ["NE", "SW", "SE"]),
|
|
22
|
+
("Country codes", ["FR", "JP", "BR"], ["FR", "JP", "BR"]),
|
|
23
|
+
("Boolean values", [True, False], ['True', 'False']),
|
|
24
|
+
("None value", [None], ['None']),
|
|
25
|
+
# Numbers and numeric types
|
|
26
|
+
("Fibonacci floats", [2.584, -4.236, 6.854], ['2.584', '-4.236', '6.854']),
|
|
27
|
+
("Complex with primes", [complex(11,0), complex(13,0)], ['(11+0j)', '(13+0j)']),
|
|
28
|
+
("Decimal and Fraction", [Decimal('3.141'), Fraction(89, 55)], ['3.141', '89/55']),
|
|
29
|
+
("NumPy primes", numpy.array([11, 13, 17]), ['11', '13', '17']),
|
|
30
|
+
# Temporal types with meaningful dates
|
|
31
|
+
("Historical date", [datetime.date(1789, 7, 14)], ['1789-07-14']), # Bastille Day
|
|
32
|
+
("Time zones", [datetime.time(23, 11, 37)], ['23:11:37']), # Non-standard time
|
|
33
|
+
("Moon landing", [datetime.datetime(1969, 7, 20, 20, 17, 40)], ['1969-07-20 20:17:40']),
|
|
34
|
+
# Binary data - accepting either representation
|
|
35
|
+
("Prime bytes", [b'\x0B', b'\x0D', b'\x11'], [repr(b'\x0b'), repr(b'\x0d'), repr(b'\x11')]), # Let Python choose representation
|
|
36
|
+
("Custom bytearray", [bytearray(b"DEADBEEF")], ["bytearray(b'DEADBEEF')"]),
|
|
37
|
+
# Nested structures with unique values
|
|
38
|
+
("Nested dictionary", {'phi': 1.618, 'euler': 2.718}, ['phi', '1.618', 'euler', '2.718']),
|
|
39
|
+
("Mixed nesting", [{'NE': 37}, {'SW': 41}], ['NE', '37', 'SW', '41']),
|
|
40
|
+
("Tuples and lists", [(13, 17), [19, 23]], ['13', '17', '19', '23']),
|
|
41
|
+
("Sets and frozensets", [{37, 41}, frozenset([43, 47])], ['41', '37', '43', '47']),
|
|
42
|
+
# Special cases and error handling
|
|
43
|
+
("NaN and Infinities", [float('nan'), float('inf'), -float('inf')], ['nan', 'inf', '-inf']),
|
|
44
|
+
("Large prime", [10**19 + 33], ['10000000000000000033']),
|
|
45
|
+
("Simple recursive", [[[...]]], ['Ellipsis']), # Recursive list
|
|
46
|
+
("Complex recursive", {'self': {'self': None}}, ['self', 'self', 'None']),
|
|
47
|
+
# Generators and custom iterables
|
|
48
|
+
("Generator from primes", (x for x in [11, 13, 17]), ['11', '13', '17']),
|
|
49
|
+
("Iterator from Fibonacci", iter([3, 5, 8, 13]), ['3', '5', '8', '13']),
|
|
50
|
+
("Custom iterable cardinal", CustomIterable(["NW", "SE", "NE"]), ["NW", "SE", "NE"]),
|
|
51
|
+
("Custom iterable empty", CustomIterable([]), []),
|
|
52
|
+
# Weird stuff
|
|
53
|
+
("Bad __str__", type('BadStr', (), {'__str__': lambda x: None})(), [None]),
|
|
54
|
+
# Error cases
|
|
55
|
+
("Raising __str__", type('RaisingStr', (), {'__str__': lambda x: 1/0})(), ZeroDivisionError),
|
|
56
|
+
], ids=lambda x: x if isinstance(x, str) else "")
|
|
57
|
+
def testStringItUp(description: str, value_scrapPile: list[Any], expected: list[str] | type[Exception]) -> None:
|
|
58
|
+
"""Test stringItUp with various inputs."""
|
|
59
|
+
standardizedEqualTo(expected, stringItUp, value_scrapPile)
|
|
60
|
+
|
|
61
|
+
@pytest.mark.parametrize("description,value_scrapPile,expected", [
|
|
62
|
+
("Memory view", memoryview(b"DEADBEEF"), ["<memory at 0x"]), # Special handling for memoryview
|
|
63
|
+
], ids=lambda x: x if isinstance(x, str) else "")
|
|
64
|
+
def testStringItUpErrorCases(description: Literal['Memory view'], value_scrapPile: memoryview, expected: list[str]) -> None:
|
|
65
|
+
result = stringItUp(value_scrapPile)
|
|
66
|
+
assert len(result) == 1
|
|
67
|
+
assert result[0].startswith(expected[0])
|
|
68
|
+
|
|
69
|
+
@pytest.mark.parametrize("description,value_dictionaryLists,keywordArguments,expected", [
|
|
70
|
+
("Mixed value types", ({'ne': [11, 'prime'], 'sw': [True, None]}, {'ne': [3.141, 'golden'], 'sw': [False, 'void']}), {'destroyDuplicates': False, 'reorderLists': False}, {'ne': [11, 'prime', 3.141, 'golden'], 'sw': [True, None, False, 'void']}),
|
|
71
|
+
("Empty dictionaries", (dict[str, list[Any]](), dict[str, list[Any]]()), dict[str, Any](), dict[str, list[Any]]()),
|
|
72
|
+
("Tuple values", ({'ne': (11, 13), 'sw': (17,)}, {'ne': (19, 23, 13, 29, 11), 'sw': (31, 17, 37)}), {'destroyDuplicates': False, 'reorderLists': False}, {'ne': [11, 13, 19, 23, 13, 29, 11], 'sw': [17, 31, 17, 37]}),
|
|
73
|
+
("Set values", ({'ne': {11, 13}, 'sw': {17}}, {'ne': {19, 23, 13, 29, 11}, 'sw': {31, 17, 37}}), {'destroyDuplicates': True, 'reorderLists': True}, {'ne': [11, 13, 19, 23, 29], 'sw': [17, 31, 37]}),
|
|
74
|
+
("NumPy arrays", ({'ne': numpy.array([11, 13]), 'sw': numpy.array([17])}, {'ne': numpy.array([19, 23, 13, 29, 11]), 'sw': numpy.array([31, 17, 37])}), {'destroyDuplicates': False, 'reorderLists': False}, {'ne': [11, 13, 19, 23, 13, 29, 11], 'sw': [17, 31, 17, 37]}),
|
|
75
|
+
("Destroy duplicates", ({'fr': [11, 13], 'jp': [17]}, {'fr': [19, 23, 13, 29, 11], 'jp': [31, 17, 37]}), {'destroyDuplicates': True, 'reorderLists': False}, {'fr': [11, 13, 19, 23, 29], 'jp': [17, 31, 37]}),
|
|
76
|
+
("Non-string keys", ({None: [13], True: [17]}, {19: [23], (29, 31): [37]}), {'destroyDuplicates': False, 'reorderLists': False}, {'None': [13], 'True': [17], '19': [23], '(29, 31)': [37]}),
|
|
77
|
+
("Reorder lists", ({'fr': [11, 13], 'jp': [17]}, {'fr': [19, 23, 13, 29, 11], 'jp': [31, 17, 37]}), {'destroyDuplicates': False, 'reorderLists': True}, {'fr': [11, 11, 13, 13, 19, 23, 29], 'jp': [17, 17, 31, 37]}),
|
|
78
|
+
("Non-iterable values", ({'ne': 13, 'sw': 17}, {'ne': 19, 'nw': 23}), {'destroyDuplicates': False, 'reorderLists': False}, TypeError),
|
|
79
|
+
("Skip erroneous types", ({'ne': [11, 13], 'sw': [17, 19]}, {'ne': 23, 'nw': 29}), {'killErroneousDataTypes': True}, {'ne': [11, 13], 'sw': [17, 19]}),
|
|
80
|
+
], ids=lambda x: x if isinstance(x, str) else "")
|
|
81
|
+
def testUpdateExtendPolishDictionaryLists(description: str, value_dictionaryLists: dict[str, Any], keywordArguments: dict[str, Any], expected: dict[str, Any] | type[TypeError]) -> None:
|
|
82
|
+
standardizedEqualTo(expected, updateExtendPolishDictionaryLists, *value_dictionaryLists, **keywordArguments)
|
|
83
|
+
# ruff: noqa: ERA001
|
|
84
|
+
# NOTE one line of code with `standardizedEqualTo` replaced the following ten lines of code.
|
|
85
|
+
# if isinstance(expected, type) and issubclass(expected, Exception):
|
|
86
|
+
# with pytest.raises(expected):
|
|
87
|
+
# updateExtendPolishDictionaryLists(*value_dictionaryLists, **keywordArguments)
|
|
88
|
+
# else:
|
|
89
|
+
# result = updateExtendPolishDictionaryLists(*value_dictionaryLists, **keywordArguments)
|
|
90
|
+
# if description == "Set values": # Special handling for unordered sets
|
|
91
|
+
# for key in result:
|
|
92
|
+
# assert sorted(result[key]) == sorted(expected[key])
|
|
93
|
+
# else:
|
|
94
|
+
# assert result == expected
|
|
95
|
+
|
|
96
|
+
# ruff: noqa: RUF005
|
|
97
|
+
@pytest.mark.parametrize("description,value_arrayTarget,expected", [
|
|
98
|
+
("One range", numpy.array(list(range(50,60))), "[*range(50,60)]"),
|
|
99
|
+
("Value, range", numpy.array([123]+list(range(71,81))), "[123,*range(71,81)]"),
|
|
100
|
+
("range, value", numpy.array(list(range(91,97))+[101]), "[*range(91,97),101]"),
|
|
101
|
+
("Value, range, value", numpy.array([151]+list(range(163,171))+[181]), "[151,*range(163,171),181]"),
|
|
102
|
+
("Repeat values", numpy.array([191, 191, 191]), "[191]*3"),
|
|
103
|
+
("Value with repeat", numpy.array([211, 223, 223, 223]), "[211]+[223]*3"),
|
|
104
|
+
("Range with repeat", numpy.array(list(range(251,257))+[271, 271, 271]), "[*range(251,257)]+[271]*3"),
|
|
105
|
+
("Value, range, repeat", numpy.array([281]+list(range(291,297))+[307, 307]), "[281,*range(291,297)]+[307]*2"),
|
|
106
|
+
("repeat, value", numpy.array([313, 313, 313, 331, 331, 349]), "[313]*3+[331]*2+[349]"),
|
|
107
|
+
("repeat, range", numpy.array([373, 373, 373]+list(range(383,389))), "[373]*3+[*range(383,389)]"),
|
|
108
|
+
("repeat, range, value", numpy.array(7*[401]+list(range(409,415))+[421]), "[401]*7+[*range(409,415),421]"),
|
|
109
|
+
("Repeated primes", numpy.array([431, 431, 431, 443, 443, 457]), "[431]*3+[443]*2+[457]"),
|
|
110
|
+
("Two Ranges", numpy.array(list(range(461,471))+list(range(479,487))), "[*range(461,471),*range(479,487)]"),
|
|
111
|
+
("2D array primes", numpy.array([[491, 499, 503], [509, 521, 523]]), "[[491,499,503],[509,521,523]]"),
|
|
112
|
+
("3D array primes", numpy.array([[[541, 547], [557, 563]], [[569, 571], [577, 587]]]), "[[[541,547],[557,563]],[[569,571],[577,587]]]"),
|
|
113
|
+
], ids=lambda x: x if isinstance(x, str) else "")
|
|
114
|
+
def testAutoDecodingRLE(description: str, value_arrayTarget: NDArray[numpy.integer[Any]], expected: str) -> None:
|
|
115
|
+
"""Test autoDecodingRLE with various input arrays."""
|
|
116
|
+
standardizedEqualTo(expected, autoDecodingRLE, value_arrayTarget)
|
|
117
|
+
|
|
118
|
+
# Helper functions for generating RLE test data
|
|
119
|
+
def generateCartesianMapping(dimensions: tuple[int, int], formula: Callable[[int, int], int]) -> NDArray[Any]:
|
|
120
|
+
"""Generate a 2D cartesian mapping based on a formula."""
|
|
121
|
+
height, width = dimensions
|
|
122
|
+
arrayMapping = numpy.zeros((height, width), dtype=numpy.int32)
|
|
123
|
+
|
|
124
|
+
for y in range(height):
|
|
125
|
+
for x in range(width):
|
|
126
|
+
arrayMapping[y, x] = formula(x, y)
|
|
127
|
+
|
|
128
|
+
return arrayMapping
|
|
129
|
+
|
|
130
|
+
def generateWavePattern(dimensions: tuple[int, int]) -> NDArray[Any]:
|
|
131
|
+
"""Generate a sine wave pattern that produces many RLE-friendly sequences."""
|
|
132
|
+
height, width = dimensions
|
|
133
|
+
|
|
134
|
+
def waveFormula(x: int, y: int) -> int:
|
|
135
|
+
return int(10 * numpy.sin(x / 5) + 10 * numpy.sin(y / 5))
|
|
136
|
+
|
|
137
|
+
return generateCartesianMapping(dimensions, waveFormula)
|
|
138
|
+
|
|
139
|
+
def generateChessboard(dimensions: tuple[int, int], squareSize: int = 4) -> NDArray[Any]:
|
|
140
|
+
"""Generate a chessboard pattern with alternating values."""
|
|
141
|
+
height, width = dimensions
|
|
142
|
+
|
|
143
|
+
def chessboardFormula(x: int, y: int) -> int:
|
|
144
|
+
return 1 if ((x // squareSize) + (y // squareSize)) % 2 == 0 else 0
|
|
145
|
+
|
|
146
|
+
return generateCartesianMapping(dimensions, chessboardFormula)
|
|
147
|
+
|
|
148
|
+
def generatePrimeModuloMatrix(dimensions: tuple[int, int], modulus: int = 6) -> NDArray[Any]:
|
|
149
|
+
"""Generate a matrix where each cell is (x*y) % modulus."""
|
|
150
|
+
height, width = dimensions
|
|
151
|
+
|
|
152
|
+
def primeModuloFormula(x: int, y: int) -> int:
|
|
153
|
+
return ((x + 1) * (y + 1)) % modulus
|
|
154
|
+
|
|
155
|
+
return generateCartesianMapping(dimensions, primeModuloFormula)
|
|
156
|
+
|
|
157
|
+
def generateSpiralPattern(dimensions: tuple[int, int], scale: int = 1) -> NDArray[Any]:
|
|
158
|
+
"""Generate a spiral pattern that creates interesting RLE sequences."""
|
|
159
|
+
height, width = dimensions
|
|
160
|
+
|
|
161
|
+
def spiralFormula(x: int, y: int) -> int:
|
|
162
|
+
xCenter = width // 2
|
|
163
|
+
yCenter = height // 2
|
|
164
|
+
distanceX = x - xCenter
|
|
165
|
+
distanceY = y - yCenter
|
|
166
|
+
distance = numpy.sqrt(distanceX**2 + distanceY**2)
|
|
167
|
+
angle = numpy.arctan2(distanceY, distanceX)
|
|
168
|
+
return int((distance + 5 * angle) * scale) % 10
|
|
169
|
+
|
|
170
|
+
return generateCartesianMapping(dimensions, spiralFormula)
|
|
171
|
+
|
|
172
|
+
def generateSignedQuadraticFunction(dimensions: tuple[int, int]) -> NDArray[Any]:
|
|
173
|
+
"""Generate a matrix with a quadratic function that includes negative values."""
|
|
174
|
+
height, width = dimensions
|
|
175
|
+
|
|
176
|
+
def quadraticFormula(x: int, y: int) -> int:
|
|
177
|
+
xCenter = width // 2
|
|
178
|
+
yCenter = height // 2
|
|
179
|
+
return (x - xCenter)**2 - (y - yCenter)**2
|
|
180
|
+
|
|
181
|
+
return generateCartesianMapping(dimensions, quadraticFormula)
|
|
182
|
+
|
|
183
|
+
def generateTilePattern(dimensions: tuple[int, int], tileSize: int = 10) -> NDArray[Any]:
|
|
184
|
+
"""Generate a repeating tile pattern."""
|
|
185
|
+
height, width = dimensions
|
|
186
|
+
|
|
187
|
+
def tileFormula(x: int, y: int) -> int:
|
|
188
|
+
patternX = x % tileSize
|
|
189
|
+
patternY = y % tileSize
|
|
190
|
+
if patternX < patternY:
|
|
191
|
+
return patternX
|
|
192
|
+
else:
|
|
193
|
+
return patternY
|
|
194
|
+
|
|
195
|
+
return generateCartesianMapping(dimensions, tileFormula)
|
|
196
|
+
|
|
197
|
+
def generateRepeatingZones(dimensions: tuple[int, int], zoneWidth: int = 15) -> NDArray[Any]:
|
|
198
|
+
"""Generate horizontal zones with repeating values."""
|
|
199
|
+
height, width = dimensions
|
|
200
|
+
|
|
201
|
+
def zoneFormula(x: int, y: int) -> int:
|
|
202
|
+
zone = y // zoneWidth
|
|
203
|
+
return zone % 5 # 5 different zones
|
|
204
|
+
|
|
205
|
+
return generateCartesianMapping(dimensions, zoneFormula)
|
|
206
|
+
|
|
207
|
+
def generateStepPattern(dimensions: tuple[int, int], step: int = 5) -> NDArray[Any]:
|
|
208
|
+
"""Generate a stepping pattern that increases along the x-axis."""
|
|
209
|
+
height, width = dimensions
|
|
210
|
+
|
|
211
|
+
def stepFormula(x: int, y: int) -> int:
|
|
212
|
+
return x // step
|
|
213
|
+
|
|
214
|
+
return generateCartesianMapping(dimensions, stepFormula)
|
|
215
|
+
|
|
216
|
+
def generateAlternatingColumns(dimensions: tuple[int, int], blockSize: int = 1) -> NDArray[Any]:
|
|
217
|
+
"""Generate alternating columns with different values."""
|
|
218
|
+
height, width = dimensions
|
|
219
|
+
|
|
220
|
+
def columnFormula(x: int, y: int) -> int:
|
|
221
|
+
return (x // blockSize) % 2
|
|
222
|
+
|
|
223
|
+
return generateCartesianMapping(dimensions, columnFormula)
|
|
224
|
+
|
|
225
|
+
# Updated test cases for autoDecodingRLE with more realistic data
|
|
226
|
+
@pytest.mark.parametrize("description,value_arrayTarget", [
|
|
227
|
+
# Basic test cases with simple patterns
|
|
228
|
+
("Simple range", numpy.array(list(range(50,60)))),
|
|
229
|
+
|
|
230
|
+
# Chessboard patterns
|
|
231
|
+
("Small chessboard", generateChessboard((8, 8))),
|
|
232
|
+
|
|
233
|
+
# Alternating columns - creates patterns with good RLE opportunities
|
|
234
|
+
("Alternating columns", generateAlternatingColumns((5, 20), 2)),
|
|
235
|
+
|
|
236
|
+
# Step pattern - creates horizontal runs
|
|
237
|
+
("Step pattern", generateStepPattern((6, 30), 3)),
|
|
238
|
+
|
|
239
|
+
# Repeating zones - creates horizontal bands
|
|
240
|
+
("Repeating zones", generateRepeatingZones((40, 40), 8)),
|
|
241
|
+
|
|
242
|
+
# Tile pattern - creates complex repeating regions
|
|
243
|
+
("Tile pattern", generateTilePattern((15, 15), 5)),
|
|
244
|
+
|
|
245
|
+
# Signed quadratic function - includes negative values
|
|
246
|
+
("Signed quadratic", generateSignedQuadraticFunction((10, 10))),
|
|
247
|
+
|
|
248
|
+
# Prime modulo matrix - periodic patterns
|
|
249
|
+
("Prime modulo", generatePrimeModuloMatrix((12, 12), 7)),
|
|
250
|
+
|
|
251
|
+
# Wave pattern - smooth gradients
|
|
252
|
+
("Wave pattern", generateWavePattern((20, 20))),
|
|
253
|
+
|
|
254
|
+
# Spiral pattern - complex pattern with good RLE potential
|
|
255
|
+
("Spiral pattern", generateSpiralPattern((15, 15), 2)),
|
|
256
|
+
], ids=lambda x: x if isinstance(x, str) else "")
|
|
257
|
+
def testAutoDecodingRLEWithRealisticData(description: str, value_arrayTarget: NDArray[numpy.integer[Any]]) -> None:
|
|
258
|
+
"""Test autoDecodingRLE with more realistic data patterns."""
|
|
259
|
+
# Here we test the function behavior rather than expected string output
|
|
260
|
+
resultRLE = autoDecodingRLE(value_arrayTarget)
|
|
261
|
+
|
|
262
|
+
# Test that the result is a valid string
|
|
263
|
+
assert isinstance(resultRLE, str)
|
|
264
|
+
|
|
265
|
+
# Test that the result contains the expected syntax elements
|
|
266
|
+
assert "[" in resultRLE, f"Result should contain list syntax: {resultRLE}"
|
|
267
|
+
assert "]" in resultRLE, f"Result should contain list syntax: {resultRLE}"
|
|
268
|
+
|
|
269
|
+
# Check that the result is more compact than the raw string representation
|
|
270
|
+
rawStrLength = len(str(value_arrayTarget.tolist()))
|
|
271
|
+
encodedLength = len(resultRLE)
|
|
272
|
+
assert encodedLength <= rawStrLength, f"Encoded string ({encodedLength}) should be shorter than raw string ({rawStrLength})"
|
|
273
|
+
|
|
274
|
+
@pytest.mark.parametrize("description,addSpaces", [
|
|
275
|
+
("With spaces", True),
|
|
276
|
+
("Without spaces", False),
|
|
277
|
+
], ids=lambda x: x if isinstance(x, str) else "")
|
|
278
|
+
def testAutoDecodingRLEWithSpaces(description: str, addSpaces: bool) -> None:
|
|
279
|
+
"""Test that the addSpaces parameter affects the internal comparison logic.
|
|
280
|
+
|
|
281
|
+
Note: addSpaces doesn't directly change the output format, it just changes
|
|
282
|
+
the comparison when measuring the length of the string representation.
|
|
283
|
+
The feature exists because `ast` inserts spaces in its string representation.
|
|
284
|
+
"""
|
|
285
|
+
# Create a pattern that has repeated sequences to trigger the RLE logic
|
|
286
|
+
arrayTarget = generateRepeatingZones((10, 10), 2)
|
|
287
|
+
|
|
288
|
+
# Test both configurations
|
|
289
|
+
resultWithSpacesFlag = autoDecodingRLE(arrayTarget, assumeAddSpaces=addSpaces)
|
|
290
|
+
resultNoSpacesFlag = autoDecodingRLE(arrayTarget, assumeAddSpaces=False)
|
|
291
|
+
|
|
292
|
+
# When addSpaces=True, the internal length comparisons change
|
|
293
|
+
# but the actual output format doesn't necessarily differ
|
|
294
|
+
# Just verify the function runs without errors in both cases
|
|
295
|
+
assert isinstance(resultWithSpacesFlag, str)
|
|
296
|
+
assert isinstance(resultNoSpacesFlag, str)
|
|
297
|
+
|
|
298
|
+
def testAutoDecodingRLELargeCartesianMapping() -> None:
|
|
299
|
+
"""Test autoDecodingRLE with a large (100x100) cartesian mapping."""
|
|
300
|
+
dimensions = (100, 100)
|
|
301
|
+
|
|
302
|
+
# Generate a large cartesian mapping with a complex pattern
|
|
303
|
+
def complexFormula(x: int, y: int) -> int:
|
|
304
|
+
return ((x * 17) % 11 + (y * 13) % 7) % 10
|
|
305
|
+
|
|
306
|
+
arrayMapping = generateCartesianMapping(dimensions, complexFormula)
|
|
307
|
+
|
|
308
|
+
# Verify the function works with large arrays
|
|
309
|
+
resultRLE = autoDecodingRLE(arrayMapping)
|
|
310
|
+
|
|
311
|
+
# The result should be a valid string representation
|
|
312
|
+
assert isinstance(resultRLE, str)
|
|
313
|
+
assert "[" in resultRLE
|
|
314
|
+
assert "]" in resultRLE
|
|
315
|
+
|
|
316
|
+
# The RLE encoding should be more compact than the raw representation
|
|
317
|
+
rawStrLength = len(str(arrayMapping.tolist()))
|
|
318
|
+
encodedLength = len(resultRLE)
|
|
319
|
+
assert encodedLength <= rawStrLength, f"RLE encoded string ({encodedLength}) should be shorter than raw string ({rawStrLength})"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# pyright: standard
|
|
2
|
+
from hunterMakesPy import importLogicalPath2Identifier, importPathFilename2Identifier, makeDirsSafely, writeStringToHere
|
|
3
|
+
from 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() == "hello world", uniformTestFailureMessage("hello world", filePath.read_text(), "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( "source, identifier, expected", [ ("def fibonacciNumber():\n return 13\n", "fibonacciNumber", 13), ("prime = 17\n", "prime", 17), ] )
|
|
35
|
+
def testImportPathFilename2Identifier(tmp_path: pathlib.Path, source: str, identifier: str, expected: object) -> None:
|
|
36
|
+
filePath = tmp_path / "moduleTest.py"
|
|
37
|
+
filePath.write_text(source)
|
|
38
|
+
imported = importPathFilename2Identifier(filePath, identifier)
|
|
39
|
+
if callable(imported):
|
|
40
|
+
actual = imported()
|
|
41
|
+
else:
|
|
42
|
+
actual = imported
|
|
43
|
+
assert actual == expected, uniformTestFailureMessage(expected, actual, "testImportPathFilename2Identifier", (filePath, identifier))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# pyright: standard
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from hunterMakesPy.pytestForYourUse import (
|
|
4
|
+
PytestFor_defineConcurrencyLimit, PytestFor_intInnit, PytestFor_oopsieKwargsie)
|
|
5
|
+
from typing import ParamSpec, TypeVar
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
parameters = ParamSpec('parameters')
|
|
9
|
+
returnType = TypeVar('returnType')
|
|
10
|
+
|
|
11
|
+
@pytest.mark.parametrize("nameOfTest,aPytest", PytestFor_defineConcurrencyLimit())
|
|
12
|
+
def testConcurrencyLimit(nameOfTest: str, aPytest: Callable[parameters, returnType], *arguments: parameters.args, **keywordArguments: parameters.kwargs) -> None:
|
|
13
|
+
aPytest(*arguments, **keywordArguments)
|
|
14
|
+
|
|
15
|
+
@pytest.mark.parametrize("nameOfTest,aPytest", PytestFor_intInnit())
|
|
16
|
+
def testIntInnit(nameOfTest: str, aPytest: Callable[parameters, returnType], *arguments: parameters.args, **keywordArguments: parameters.kwargs) -> None:
|
|
17
|
+
aPytest(*arguments, **keywordArguments)
|
|
18
|
+
|
|
19
|
+
@pytest.mark.parametrize("nameOfTest,aPytest", PytestFor_oopsieKwargsie())
|
|
20
|
+
def testOopsieKwargsie(nameOfTest: str, aPytest: Callable[parameters, returnType], *arguments: parameters.args, **keywordArguments: parameters.kwargs) -> None:
|
|
21
|
+
aPytest(*arguments, **keywordArguments)
|