mapFolding 0.7.0__tar.gz → 0.7.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.
- {mapfolding-0.7.0 → mapfolding-0.7.1}/PKG-INFO +1 -1
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/__init__.py +1 -1
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/basecamp.py +2 -2
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/beDRY.py +13 -6
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/filesystem.py +37 -29
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/Z0Z_workbench.py +6 -7
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeDataConverters.py +9 -9
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/transformationTools.py +1 -1
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/whatWillBe.py +144 -98
- mapfolding-0.7.0/mapFolding/theDao.py → mapfolding-0.7.1/mapFolding/syntheticModules/multiprocessingCount_doTheNeedful.py +40 -32
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCountSequential.py +4 -3
- mapfolding-0.7.1/mapFolding/theDao.py +219 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/theSSOT.py +45 -30
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/PKG-INFO +1 -1
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/SOURCES.txt +2 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/pyproject.toml +6 -5
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_computations.py +9 -5
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_tasks.py +9 -3
- {mapfolding-0.7.0 → mapfolding-0.7.1}/LICENSE +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/README.md +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/noHomeYet.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/oeis.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/py.typed +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/flattened.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/hunterNumba.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/irvineJavaPort.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/jax.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/lunnan.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/lunnanNumpy.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/lunnanWhile.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/rotatedEntryPoint.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/__init__.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/__init__.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/dataNamespaceFlattened.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCount.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCountExample.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numba_doTheNeedful.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numba_doTheNeedfulExample.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/dependency_links.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/entry_points.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/requires.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/top_level.txt +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/setup.cfg +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/__init__.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/conftest.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_filesystem.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_oeis.py +0 -0
- {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_other.py +0 -0
|
@@ -43,8 +43,8 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
43
43
|
concurrencyLimit: int = setCPUlimit(CPUlimit)
|
|
44
44
|
computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
computationStateComplete: ComputationState =
|
|
46
|
+
dispatcherCallableProxy = getPackageDispatcher()
|
|
47
|
+
computationStateComplete: ComputationState = dispatcherCallableProxy(computationStateInitialized)
|
|
48
48
|
|
|
49
49
|
computationStateComplete.getFoldsTotal()
|
|
50
50
|
|
|
@@ -73,7 +73,14 @@ def makeDataContainer(shape: int | tuple[int, ...], datatype: type[numpy.signedi
|
|
|
73
73
|
return numpy.zeros(shape, dtype=numpyDtype)
|
|
74
74
|
|
|
75
75
|
def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
76
|
-
"""Sets CPU limit for
|
|
76
|
+
"""Sets CPU limit for concurrent operations.
|
|
77
|
+
|
|
78
|
+
If the concurrency is managed by `numba`, the maximum number of CPUs is retrieved from `numba.get_num_threads()` and not by polling the hardware. Therefore, if there are
|
|
79
|
+
numba environment variables limiting the number of available CPUs, that will effect this function. That _should_ be a good thing: you control the number of CPUs available
|
|
80
|
+
to numba. But if you're not aware of that, you might be surprised by the results.
|
|
81
|
+
|
|
82
|
+
If you are designing custom modules that use numba, note that you must call `numba.set_num_threads()` (i.e., this function) before executing an `import` statement
|
|
83
|
+
on a Numba-jitted function. Otherwise, the `numba.set_num_threads()` call will have no effect on the imported function.
|
|
77
84
|
|
|
78
85
|
Parameters:
|
|
79
86
|
CPUlimit: whether and how to limit the CPU usage. See notes for details.
|
|
@@ -93,17 +100,17 @@ def setCPUlimit(CPUlimit: Any | None) -> int:
|
|
|
93
100
|
if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
|
|
94
101
|
CPUlimit = oopsieKwargsie(CPUlimit)
|
|
95
102
|
|
|
96
|
-
concurrencyLimit: int = int(defineConcurrencyLimit(CPUlimit))
|
|
97
103
|
from mapFolding.theSSOT import concurrencyPackage
|
|
98
104
|
if concurrencyPackage == 'numba':
|
|
99
105
|
from numba import get_num_threads, set_num_threads
|
|
106
|
+
concurrencyLimit: int = defineConcurrencyLimit(CPUlimit, get_num_threads())
|
|
100
107
|
set_num_threads(concurrencyLimit)
|
|
101
108
|
concurrencyLimit = get_num_threads()
|
|
102
109
|
elif concurrencyPackage == 'algorithm':
|
|
103
|
-
|
|
110
|
+
# When to use multiprocessing.set_start_method https://github.com/hunterhogan/mapFolding/issues/6
|
|
111
|
+
concurrencyLimit: int = defineConcurrencyLimit(CPUlimit)
|
|
104
112
|
else:
|
|
105
|
-
raise NotImplementedError("
|
|
106
|
-
|
|
113
|
+
raise NotImplementedError(f"I received {concurrencyPackage=} but I don't know what to do with that.")
|
|
107
114
|
return concurrencyLimit
|
|
108
115
|
|
|
109
116
|
def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, leavesTotal: int) -> int:
|
|
@@ -160,5 +167,5 @@ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: i
|
|
|
160
167
|
def outfitCountFolds(mapShape: tuple[int, ...], computationDivisions: int | str | None = None, concurrencyLimit: int = 1) -> ComputationState:
|
|
161
168
|
leavesTotal = getLeavesTotal(mapShape)
|
|
162
169
|
taskDivisions = getTaskDivisions(computationDivisions, concurrencyLimit, leavesTotal)
|
|
163
|
-
computationStateInitialized = ComputationState(mapShape, leavesTotal, taskDivisions)
|
|
170
|
+
computationStateInitialized = ComputationState(mapShape, leavesTotal, taskDivisions, concurrencyLimit)
|
|
164
171
|
return computationStateInitialized
|
|
@@ -1,35 +1,8 @@
|
|
|
1
1
|
"""Filesystem functions for mapFolding package."""
|
|
2
|
-
from pathlib import Path
|
|
2
|
+
from pathlib import Path, PurePath
|
|
3
|
+
from typing import Any
|
|
3
4
|
import os
|
|
4
5
|
|
|
5
|
-
def saveFoldsTotal(pathFilename: str | os.PathLike[str], foldsTotal: int) -> None:
|
|
6
|
-
"""
|
|
7
|
-
Save foldsTotal with multiple fallback mechanisms.
|
|
8
|
-
|
|
9
|
-
Parameters:
|
|
10
|
-
pathFilename: Target save location
|
|
11
|
-
foldsTotal: Critical computed value to save
|
|
12
|
-
"""
|
|
13
|
-
try:
|
|
14
|
-
pathFilenameFoldsTotal = Path(pathFilename)
|
|
15
|
-
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
16
|
-
pathFilenameFoldsTotal.write_text(str(foldsTotal))
|
|
17
|
-
except Exception as ERRORmessage:
|
|
18
|
-
try:
|
|
19
|
-
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
20
|
-
print(ERRORmessage)
|
|
21
|
-
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
22
|
-
randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
|
|
23
|
-
filenameInfixUnique = ''.join(randomnessPlanB)
|
|
24
|
-
pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
|
|
25
|
-
writeStreamFallback = open(pathFilenamePlanB, 'w')
|
|
26
|
-
writeStreamFallback.write(str(foldsTotal))
|
|
27
|
-
writeStreamFallback.close()
|
|
28
|
-
print(str(pathFilenamePlanB))
|
|
29
|
-
except Exception:
|
|
30
|
-
print(foldsTotal)
|
|
31
|
-
return None
|
|
32
|
-
|
|
33
6
|
def getFilenameFoldsTotal(mapShape: tuple[int, ...]) -> str:
|
|
34
7
|
"""Imagine your computer has been counting folds for 9 days, and when it tries to save your newly discovered value,
|
|
35
8
|
the filename is invalid. I bet you think this function is more important after that thought experiment.
|
|
@@ -85,3 +58,38 @@ def getPathFilenameFoldsTotal(mapShape: tuple[int, ...], pathLikeWriteFoldsTotal
|
|
|
85
58
|
|
|
86
59
|
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
87
60
|
return pathFilenameFoldsTotal
|
|
61
|
+
|
|
62
|
+
def saveFoldsTotal(pathFilename: str | os.PathLike[str], foldsTotal: int) -> None:
|
|
63
|
+
"""
|
|
64
|
+
Save foldsTotal with multiple fallback mechanisms.
|
|
65
|
+
|
|
66
|
+
Parameters:
|
|
67
|
+
pathFilename: Target save location
|
|
68
|
+
foldsTotal: Critical computed value to save
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
pathFilenameFoldsTotal = Path(pathFilename)
|
|
72
|
+
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
pathFilenameFoldsTotal.write_text(str(foldsTotal))
|
|
74
|
+
except Exception as ERRORmessage:
|
|
75
|
+
try:
|
|
76
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
77
|
+
print(ERRORmessage)
|
|
78
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
79
|
+
randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
|
|
80
|
+
filenameInfixUnique = ''.join(randomnessPlanB)
|
|
81
|
+
pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
|
|
82
|
+
writeStreamFallback = open(pathFilenamePlanB, 'w')
|
|
83
|
+
writeStreamFallback.write(str(foldsTotal))
|
|
84
|
+
writeStreamFallback.close()
|
|
85
|
+
print(str(pathFilenamePlanB))
|
|
86
|
+
except Exception:
|
|
87
|
+
print(foldsTotal)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def writeStringToHere(this: str, pathFilename: str | os.PathLike[Any] | PurePath) -> None:
|
|
91
|
+
"""Write the string `this` to the file at `pathFilename`."""
|
|
92
|
+
pathFilename = Path(pathFilename)
|
|
93
|
+
pathFilename.parent.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
pathFilename.write_text(str(this))
|
|
95
|
+
return None
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from mapFolding.someAssemblyRequired.synthesizeDataConverters import makeStateJob
|
|
2
1
|
from mapFolding.someAssemblyRequired.synthesizeDataConverters import makeDataclassConverter
|
|
3
2
|
from mapFolding.someAssemblyRequired.whatWillBe import IngredientsFunction, IngredientsModule, numbaFlow
|
|
4
3
|
from mapFolding.someAssemblyRequired.synthesizeCountingFunctions import Z0Z_makeCountingFunction
|
|
@@ -6,13 +5,13 @@ import ast
|
|
|
6
5
|
|
|
7
6
|
if __name__ == '__main__':
|
|
8
7
|
ingredientsFunctionDataConverter = makeDataclassConverter(
|
|
9
|
-
|
|
8
|
+
dataclassIdentifier=numbaFlow.sourceDataclassIdentifier
|
|
10
9
|
, logicalPathModuleDataclass=numbaFlow.logicalPathModuleDataclass
|
|
11
|
-
,
|
|
10
|
+
, dataclassInstance=numbaFlow.dataclassInstance
|
|
12
11
|
|
|
13
|
-
,
|
|
12
|
+
, dispatcherCallable=numbaFlow.dispatcherCallable
|
|
14
13
|
, logicalPathModuleDispatcher=numbaFlow.logicalPathModuleDispatcher
|
|
15
|
-
,
|
|
14
|
+
, dataConverterCallable=numbaFlow.dataConverterCallable
|
|
16
15
|
)
|
|
17
16
|
|
|
18
17
|
# initialize with theDao
|
|
@@ -20,7 +19,7 @@ if __name__ == '__main__':
|
|
|
20
19
|
ingredientsFunctionDataConverter.FunctionDef.body.insert(0, ast.parse(dataInitializationHack).body[0])
|
|
21
20
|
ingredientsFunctionDataConverter.imports.addImportFromStr('mapFolding.someAssemblyRequired', 'makeStateJob')
|
|
22
21
|
|
|
23
|
-
ingredientsSequential = Z0Z_makeCountingFunction(numbaFlow.
|
|
22
|
+
ingredientsSequential = Z0Z_makeCountingFunction(numbaFlow.sequentialCallable
|
|
24
23
|
, numbaFlow.sourceAlgorithm
|
|
25
24
|
, inline=True
|
|
26
25
|
, dataclass=False)
|
|
@@ -28,7 +27,7 @@ if __name__ == '__main__':
|
|
|
28
27
|
ingredientsModuleDataConverter = IngredientsModule(
|
|
29
28
|
name=numbaFlow.dataConverterModule,
|
|
30
29
|
ingredientsFunction=ingredientsFunctionDataConverter,
|
|
31
|
-
logicalPathINFIX=numbaFlow.
|
|
30
|
+
logicalPathINFIX=numbaFlow.Z0Z_flowLogicalPathRoot,
|
|
32
31
|
)
|
|
33
32
|
|
|
34
33
|
ingredientsModuleDataConverter.writeModule()
|
{mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeDataConverters.py
RENAMED
|
@@ -67,19 +67,19 @@ def shatter_dataclassesDOTdataclass(logicalPathModule: strDotStrCuzPyStoopid, da
|
|
|
67
67
|
astTupleForAssignTargetsToFragments: ast.Tuple = Make.astTuple(list_astNameDataclassFragments, ast.Store())
|
|
68
68
|
return astNameDataclass, ledgerDataclassAndFragments, list_astAnnAssign, list_astNameDataclassFragments, listKeywordForDataclassInitialization, astTupleForAssignTargetsToFragments
|
|
69
69
|
|
|
70
|
-
def makeDataclassConverter(
|
|
70
|
+
def makeDataclassConverter(dataclassIdentifier: str,
|
|
71
71
|
logicalPathModuleDataclass: str,
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
dataclassInstance: str,
|
|
73
|
+
dispatcherCallable: str,
|
|
74
74
|
logicalPathModuleDispatcher: str,
|
|
75
|
-
|
|
75
|
+
dataConverterCallable: str,
|
|
76
76
|
) -> IngredientsFunction:
|
|
77
77
|
|
|
78
|
-
astNameDataclass, ledgerDataclassAndFragments, list_astAnnAssign, list_astNameDataclassFragments, list_astKeywordDataclassFragments, astTupleForAssignTargetsToFragments = shatter_dataclassesDOTdataclass(logicalPathModuleDataclass,
|
|
78
|
+
astNameDataclass, ledgerDataclassAndFragments, list_astAnnAssign, list_astNameDataclassFragments, list_astKeywordDataclassFragments, astTupleForAssignTargetsToFragments = shatter_dataclassesDOTdataclass(logicalPathModuleDataclass, dataclassIdentifier, dataclassInstance)
|
|
79
79
|
|
|
80
80
|
ingredientsFunction = IngredientsFunction(
|
|
81
|
-
FunctionDef = Make.astFunctionDef(name=
|
|
82
|
-
, argumentsSpecification=Make.astArgumentsSpecification(args=[Make.astArg(
|
|
81
|
+
FunctionDef = Make.astFunctionDef(name=dataConverterCallable
|
|
82
|
+
, argumentsSpecification=Make.astArgumentsSpecification(args=[Make.astArg(dataclassInstance, astNameDataclass)])
|
|
83
83
|
, body = cast(list[ast.stmt], list_astAnnAssign)
|
|
84
84
|
, returns = astNameDataclass
|
|
85
85
|
)
|
|
@@ -87,9 +87,9 @@ def makeDataclassConverter(dataclassIdentifierAsStr: str,
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
callToDispatcher = Make.astAssign(listTargets=[astTupleForAssignTargetsToFragments]
|
|
90
|
-
, value=Make.astCall(Make.astName(
|
|
90
|
+
, value=Make.astCall(Make.astName(dispatcherCallable), args=list_astNameDataclassFragments))
|
|
91
91
|
ingredientsFunction.FunctionDef.body.append(callToDispatcher)
|
|
92
|
-
ingredientsFunction.imports.addImportFromStr(logicalPathModuleDispatcher,
|
|
92
|
+
ingredientsFunction.imports.addImportFromStr(logicalPathModuleDispatcher, dispatcherCallable)
|
|
93
93
|
|
|
94
94
|
ingredientsFunction.FunctionDef.body.append(Make.astReturn(Make.astCall(astNameDataclass, list_astKeywords=list_astKeywordDataclassFragments)))
|
|
95
95
|
|
{mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/transformationTools.py
RENAMED
|
@@ -316,7 +316,7 @@ class Make:
|
|
|
316
316
|
|
|
317
317
|
@staticmethod
|
|
318
318
|
def astModule(body: list[ast.stmt], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
|
|
319
|
-
return ast.Module(body
|
|
319
|
+
return ast.Module(body, type_ignores)
|
|
320
320
|
|
|
321
321
|
@staticmethod
|
|
322
322
|
def astName(identifier: ast_Identifier) -> ast.Name:
|
|
@@ -4,68 +4,69 @@
|
|
|
4
4
|
- Therefore, an abstracted system for creating settings for the package
|
|
5
5
|
- And with only a little more effort, an abstracted system for creating settings to synthesize arbitrary subsets of modules for arbitrary packages
|
|
6
6
|
"""
|
|
7
|
-
from mapFolding.someAssemblyRequired.transformationTools import
|
|
7
|
+
from mapFolding.someAssemblyRequired.transformationTools import (
|
|
8
|
+
ast_Identifier,
|
|
9
|
+
executeActionUnlessDescendantMatches,
|
|
10
|
+
extractClassDef,
|
|
11
|
+
extractFunctionDef,
|
|
12
|
+
ifThis,
|
|
13
|
+
Make,
|
|
14
|
+
NodeCollector,
|
|
15
|
+
NodeReplacer,
|
|
16
|
+
strDotStrCuzPyStoopid,
|
|
17
|
+
Then,
|
|
18
|
+
)
|
|
19
|
+
from mapFolding.filesystem import writeStringToHere
|
|
8
20
|
from mapFolding.theSSOT import (
|
|
9
21
|
FREAKOUT,
|
|
10
22
|
getDatatypePackage,
|
|
11
23
|
getSourceAlgorithm,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
theDataclassIdentifier,
|
|
25
|
+
theDataclassInstance,
|
|
26
|
+
theDispatcherCallable,
|
|
15
27
|
theFileExtension,
|
|
16
28
|
theFormatStrModuleForCallableSynthetic,
|
|
17
29
|
theFormatStrModuleSynthetic,
|
|
18
30
|
theLogicalPathModuleDataclass,
|
|
19
31
|
theLogicalPathModuleDispatcherSynthetic,
|
|
32
|
+
theModuleDispatcherSynthetic,
|
|
20
33
|
theModuleOfSyntheticModules,
|
|
21
34
|
thePackageName,
|
|
22
35
|
thePathPackage,
|
|
23
|
-
|
|
36
|
+
theSourceInitializeCallable,
|
|
37
|
+
theSourceParallelCallable,
|
|
38
|
+
theSourceSequentialCallable,
|
|
24
39
|
)
|
|
25
40
|
from autoflake import fix_code as autoflake_fix_code
|
|
26
41
|
from collections import defaultdict
|
|
27
42
|
from collections.abc import Sequence
|
|
28
43
|
from inspect import getsource as inspect_getsource
|
|
29
44
|
from mapFolding.someAssemblyRequired.ingredientsNumba import parametersNumbaDEFAULT, parametersNumbaSuperJit, parametersNumbaSuperJitParallel, ParametersNumba
|
|
30
|
-
from pathlib import Path
|
|
45
|
+
from pathlib import Path, PurePosixPath
|
|
31
46
|
from types import ModuleType
|
|
32
47
|
from typing import NamedTuple
|
|
33
48
|
from Z0Z_tools import updateExtendPolishDictionaryLists
|
|
34
49
|
import ast
|
|
35
50
|
import dataclasses
|
|
36
51
|
|
|
37
|
-
"""
|
|
38
|
-
Start with what is: theDao.py
|
|
39
|
-
Create settings that can transform into what I or the user want it to be.
|
|
40
|
-
|
|
41
|
-
The simplest flow with numba is:
|
|
42
|
-
1. one module
|
|
43
|
-
2. dispatcher
|
|
44
|
-
- initialize data with makeJob
|
|
45
|
-
- smash dataclass
|
|
46
|
-
- call countSequential
|
|
47
|
-
3. countSequential
|
|
48
|
-
- jitted, not super-jitted
|
|
49
|
-
- functions inlined (or I'd have to jit them)
|
|
50
|
-
- return groupsOfFolds
|
|
51
|
-
4. recycle the dataclass with groupsOfFolds
|
|
52
|
-
5. return the dataclass
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
52
|
@dataclasses.dataclass
|
|
56
53
|
class RecipeSynthesizeFlow:
|
|
57
54
|
"""Settings for synthesizing flow."""
|
|
58
55
|
# TODO consider `IngredientsFlow` or similar
|
|
56
|
+
# ========================================
|
|
57
|
+
# Source
|
|
59
58
|
sourceAlgorithm: ModuleType = getSourceAlgorithm()
|
|
60
59
|
sourcePython: str = inspect_getsource(sourceAlgorithm)
|
|
61
|
-
# sourcePython: str = inspect_getsource(self.sourceAlgorithm)
|
|
62
|
-
# "self" is not defined
|
|
63
|
-
# I still hate the OOP paradigm. But I like this dataclass stuff.
|
|
64
60
|
source_astModule: ast.Module = ast.parse(sourcePython)
|
|
61
|
+
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
62
|
+
sourceDispatcherCallable: str = theDispatcherCallable
|
|
63
|
+
sourceSequentialCallable: str = theSourceSequentialCallable
|
|
64
|
+
sourceDataclassIdentifier: str = theDataclassIdentifier
|
|
65
|
+
# I still hate the OOP paradigm. But I like this dataclass stuff.
|
|
65
66
|
|
|
66
67
|
# ========================================
|
|
67
68
|
# Filesystem
|
|
68
|
-
pathPackage:
|
|
69
|
+
pathPackage: PurePosixPath = PurePosixPath(thePathPackage)
|
|
69
70
|
fileExtension: str = theFileExtension
|
|
70
71
|
|
|
71
72
|
# ========================================
|
|
@@ -78,19 +79,23 @@ class RecipeSynthesizeFlow:
|
|
|
78
79
|
packageName: ast_Identifier = thePackageName
|
|
79
80
|
|
|
80
81
|
# Module
|
|
81
|
-
|
|
82
|
+
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
83
|
+
Z0Z_flowLogicalPathRoot: str = theModuleOfSyntheticModules
|
|
84
|
+
moduleDispatcher: str = theModuleDispatcherSynthetic
|
|
82
85
|
logicalPathModuleDataclass: str = theLogicalPathModuleDataclass
|
|
86
|
+
# https://github.com/hunterhogan/mapFolding/issues/4
|
|
87
|
+
# `theLogicalPathModuleDispatcherSynthetic` is a problem. It is defined in theSSOT, but it can also be calculated.
|
|
83
88
|
logicalPathModuleDispatcher: str = theLogicalPathModuleDispatcherSynthetic
|
|
84
89
|
dataConverterModule: str = 'dataNamespaceFlattened'
|
|
85
90
|
|
|
86
91
|
# Function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
sequentialCallable: str = sourceSequentialCallable
|
|
93
|
+
dataclassIdentifier: str = sourceDataclassIdentifier
|
|
94
|
+
dataConverterCallable: str = 'unpackDataclassPackUp'
|
|
95
|
+
dispatcherCallable: str = sourceDispatcherCallable
|
|
91
96
|
|
|
92
97
|
# Variable
|
|
93
|
-
|
|
98
|
+
dataclassInstance: str = theDataclassInstance
|
|
94
99
|
|
|
95
100
|
class LedgerOfImports:
|
|
96
101
|
def __init__(self, startWith: ast.AST | None = None) -> None:
|
|
@@ -117,6 +122,11 @@ class LedgerOfImports:
|
|
|
117
122
|
def addImportFromStr(self, module: str, name: str, asname: str | None = None) -> None:
|
|
118
123
|
self.dictionaryImportFrom[module].append((name, asname))
|
|
119
124
|
|
|
125
|
+
def exportListModuleNames(self) -> list[str]:
|
|
126
|
+
listModuleNames: list[str] = list(self.dictionaryImportFrom.keys())
|
|
127
|
+
listModuleNames.extend(self.listImport)
|
|
128
|
+
return sorted(set(listModuleNames))
|
|
129
|
+
|
|
120
130
|
def makeListAst(self) -> list[ast.ImportFrom | ast.Import]:
|
|
121
131
|
listAstImportFrom: list[ast.ImportFrom] = []
|
|
122
132
|
|
|
@@ -147,6 +157,12 @@ class LedgerOfImports:
|
|
|
147
157
|
if isinstance(smurf, (ast.Import, ast.ImportFrom)):
|
|
148
158
|
self.addAst(smurf)
|
|
149
159
|
|
|
160
|
+
@dataclasses.dataclass
|
|
161
|
+
class Z0Z_IngredientsDataStructure:
|
|
162
|
+
"""Everything necessary to create a data structure should be here."""
|
|
163
|
+
dataclassDef: ast.ClassDef
|
|
164
|
+
imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
|
|
165
|
+
|
|
150
166
|
@dataclasses.dataclass
|
|
151
167
|
class IngredientsFunction:
|
|
152
168
|
"""Everything necessary to integrate a function into a module should be here."""
|
|
@@ -155,21 +171,89 @@ class IngredientsFunction:
|
|
|
155
171
|
|
|
156
172
|
@dataclasses.dataclass
|
|
157
173
|
class IngredientsModule:
|
|
158
|
-
"""Everything necessary to create
|
|
159
|
-
|
|
174
|
+
"""Everything necessary to create one _logical_ `ast.Module` should be here.
|
|
175
|
+
Extrinsic qualities should be handled externally, such as with `RecipeModule`."""
|
|
176
|
+
# If an `ast.Module` had a logical name that would be reasonable, but Python is firmly opposed
|
|
177
|
+
# to a reasonable namespace, therefore, Hunter, you were silly to add a `name` field to this
|
|
178
|
+
# dataclass for building an `ast.Module`.
|
|
179
|
+
# name: ast_Identifier
|
|
180
|
+
# Hey, genius, note that this is dataclasses.InitVar
|
|
160
181
|
ingredientsFunction: dataclasses.InitVar[Sequence[IngredientsFunction] | IngredientsFunction | None] = None
|
|
161
182
|
|
|
183
|
+
# `body` attribute of `ast.Module`
|
|
162
184
|
imports: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
|
|
163
185
|
prologue: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
164
186
|
functions: list[ast.FunctionDef | ast.stmt] = dataclasses.field(default_factory=list)
|
|
165
187
|
epilogue: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
166
188
|
launcher: list[ast.stmt] = dataclasses.field(default_factory=list)
|
|
167
189
|
|
|
190
|
+
# parameter for `ast.Module` constructor
|
|
191
|
+
type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
|
|
192
|
+
|
|
193
|
+
def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
|
|
194
|
+
if ingredientsFunction is not None:
|
|
195
|
+
if isinstance(ingredientsFunction, IngredientsFunction):
|
|
196
|
+
self.addIngredientsFunction(ingredientsFunction)
|
|
197
|
+
else:
|
|
198
|
+
self.addIngredientsFunction(*ingredientsFunction)
|
|
199
|
+
|
|
200
|
+
def addIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
|
|
201
|
+
"""Add one or more `IngredientsFunction`. """
|
|
202
|
+
listLedgers: list[LedgerOfImports] = []
|
|
203
|
+
for definition in ingredientsFunction:
|
|
204
|
+
self.functions.append(definition.FunctionDef)
|
|
205
|
+
listLedgers.append(definition.imports)
|
|
206
|
+
self.imports.update(*listLedgers)
|
|
207
|
+
|
|
208
|
+
def _makeModuleBody(self) -> list[ast.stmt]:
|
|
209
|
+
body: list[ast.stmt] = []
|
|
210
|
+
body.extend(self.imports.makeListAst())
|
|
211
|
+
body.extend(self.prologue)
|
|
212
|
+
body.extend(self.functions)
|
|
213
|
+
body.extend(self.epilogue)
|
|
214
|
+
body.extend(self.launcher)
|
|
215
|
+
# TODO `launcher`, if it exists, must start with `if __name__ == '__main__':` and be indented
|
|
216
|
+
return body
|
|
217
|
+
|
|
218
|
+
def export(self) -> ast.Module:
|
|
219
|
+
"""Create a new `ast.Module` from the ingredients."""
|
|
220
|
+
return Make.astModule(self._makeModuleBody(), self.type_ignores)
|
|
221
|
+
|
|
222
|
+
@dataclasses.dataclass
|
|
223
|
+
class RecipeCountingFunction:
|
|
224
|
+
"""Settings for synthesizing counting functions."""
|
|
225
|
+
ingredients: IngredientsFunction
|
|
226
|
+
|
|
227
|
+
@dataclasses.dataclass
|
|
228
|
+
class RecipeDispatchFunction:
|
|
229
|
+
# A "dispatcher" must receive a dataclass instance and return a dataclass instance.
|
|
230
|
+
# computationStateComplete: ComputationState = dispatcher(computationStateInitialized)
|
|
231
|
+
# The most critical values in the returned dataclass are foldGroups[0:-1] and leavesTotal
|
|
232
|
+
# self.foldsTotal = DatatypeFoldsTotal(self.foldGroups[0:-1].sum() * self.leavesTotal)
|
|
233
|
+
# the function name is required by IngredientsFunction
|
|
234
|
+
ingredients: IngredientsFunction
|
|
235
|
+
logicalPathModuleDataclass: str = theLogicalPathModuleDataclass
|
|
236
|
+
dataclassIdentifier: str = theDataclassIdentifier
|
|
237
|
+
dataclassInstance: str = theDataclassInstance
|
|
238
|
+
Z0Z_unpackDataclass: bool = True
|
|
239
|
+
countDispatcher: bool = True
|
|
240
|
+
# is this the countDispatcher or what is the information for calling the countDispatcher: import or no? callable identifier? parameters? return type?
|
|
241
|
+
# countDispatcher lives in `theLogicalPathModuleDispatcherSynthetic`
|
|
242
|
+
# countDispatcher is named `theDispatcherCallable`
|
|
243
|
+
# post init
|
|
244
|
+
# addImportFromStr(self, module: str, name: str, asname: str | None = None)
|
|
245
|
+
|
|
246
|
+
@dataclasses.dataclass
|
|
247
|
+
class RecipeModule:
|
|
248
|
+
"""How to get one or more logical `ast.Module` on disk as one physical module."""
|
|
249
|
+
# Physical namespace
|
|
250
|
+
filenameStem: str
|
|
251
|
+
fileExtension: str = theFileExtension
|
|
252
|
+
pathPackage: PurePosixPath = PurePosixPath(thePathPackage)
|
|
253
|
+
|
|
254
|
+
# Physical and logical namespace
|
|
168
255
|
packageName: ast_Identifier | None= thePackageName
|
|
169
256
|
logicalPathINFIX: ast_Identifier | strDotStrCuzPyStoopid | None = None # module names other than the module itself and the package name
|
|
170
|
-
pathPackage: Path = thePathPackage
|
|
171
|
-
fileExtension: str = theFileExtension
|
|
172
|
-
type_ignores: list[ast.TypeIgnore] = dataclasses.field(default_factory=list)
|
|
173
257
|
|
|
174
258
|
def _getLogicalPathParent(self) -> str | None:
|
|
175
259
|
listModules: list[ast_Identifier] = []
|
|
@@ -185,18 +269,22 @@ class IngredientsModule:
|
|
|
185
269
|
logicalPathParent: str | None = self._getLogicalPathParent()
|
|
186
270
|
if logicalPathParent:
|
|
187
271
|
listModules.append(logicalPathParent)
|
|
188
|
-
listModules.append(self.
|
|
272
|
+
listModules.append(self.filenameStem)
|
|
189
273
|
return '.'.join(listModules)
|
|
190
274
|
|
|
191
275
|
@property
|
|
192
|
-
def pathFilename(self)
|
|
193
|
-
|
|
194
|
-
|
|
276
|
+
def pathFilename(self):
|
|
277
|
+
""" `PurePosixPath` ensures os-independent formatting of the `dataclass.field` value,
|
|
278
|
+
but you must convert to `Path` to perform filesystem operations."""
|
|
279
|
+
pathRoot: PurePosixPath = self.pathPackage
|
|
280
|
+
filename: str = self.filenameStem + self.fileExtension
|
|
195
281
|
if self.logicalPathINFIX:
|
|
196
|
-
whyIsThisStillAThing = self.logicalPathINFIX.split('.')
|
|
282
|
+
whyIsThisStillAThing: list[str] = self.logicalPathINFIX.split('.')
|
|
197
283
|
pathRoot = pathRoot.joinpath(*whyIsThisStillAThing)
|
|
198
284
|
return pathRoot.joinpath(filename)
|
|
199
285
|
|
|
286
|
+
ingredients: IngredientsModule = IngredientsModule()
|
|
287
|
+
|
|
200
288
|
@property
|
|
201
289
|
def absoluteImport(self) -> ast.Import:
|
|
202
290
|
return Make.astImport(self._getLogicalPathAbsolute())
|
|
@@ -204,76 +292,33 @@ class IngredientsModule:
|
|
|
204
292
|
@property
|
|
205
293
|
def absoluteImportFrom(self) -> ast.ImportFrom:
|
|
206
294
|
""" `from . import theModule` """
|
|
207
|
-
logicalPathParent: str
|
|
208
|
-
|
|
209
|
-
logicalPathParent = '.'
|
|
210
|
-
return Make.astImportFrom(logicalPathParent, [Make.astAlias(self.name)])
|
|
211
|
-
|
|
212
|
-
def __post_init__(self, ingredientsFunction: Sequence[IngredientsFunction] | IngredientsFunction | None = None) -> None:
|
|
213
|
-
if ingredientsFunction is not None:
|
|
214
|
-
if isinstance(ingredientsFunction, IngredientsFunction):
|
|
215
|
-
self.addIngredientsFunction(ingredientsFunction)
|
|
216
|
-
else:
|
|
217
|
-
self.addIngredientsFunction(*ingredientsFunction)
|
|
218
|
-
|
|
219
|
-
def addIngredientsFunction(self, *ingredientsFunction: IngredientsFunction) -> None:
|
|
220
|
-
"""Add one or more `IngredientsFunction`. """
|
|
221
|
-
listLedgers: list[LedgerOfImports] = []
|
|
222
|
-
for definition in ingredientsFunction:
|
|
223
|
-
self.functions.append(definition.FunctionDef)
|
|
224
|
-
listLedgers.append(definition.imports)
|
|
225
|
-
self.imports.update(*listLedgers)
|
|
226
|
-
|
|
227
|
-
def _makeModuleBody(self) -> list[ast.stmt]:
|
|
228
|
-
"""Constructs the body of the module, including prologue, functions, epilogue, and launcher."""
|
|
229
|
-
body: list[ast.stmt] = []
|
|
230
|
-
body.extend(self.imports.makeListAst())
|
|
231
|
-
body.extend(self.prologue)
|
|
232
|
-
body.extend(self.functions)
|
|
233
|
-
body.extend(self.epilogue)
|
|
234
|
-
body.extend(self.launcher)
|
|
235
|
-
# TODO `launcher` must start with `if __name__ == '__main__':` and be indented
|
|
236
|
-
return body
|
|
295
|
+
logicalPathParent: str = self._getLogicalPathParent() or '.'
|
|
296
|
+
return Make.astImportFrom(logicalPathParent, [Make.astAlias(self.filenameStem)])
|
|
237
297
|
|
|
238
298
|
def writeModule(self) -> None:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
This method creates a proper AST module with imports and function definitions,
|
|
242
|
-
fixes missing locations, unpacks the AST to Python code, applies autoflake
|
|
243
|
-
to clean up imports, and writes the resulting code to the appropriate file.
|
|
244
|
-
"""
|
|
245
|
-
astModule = Make.astModule(body=self._makeModuleBody(), type_ignores=self.type_ignores)
|
|
299
|
+
astModule = self.ingredients.export()
|
|
246
300
|
ast.fix_missing_locations(astModule)
|
|
247
301
|
pythonSource: str = ast.unparse(astModule)
|
|
248
302
|
if not pythonSource: raise FREAKOUT
|
|
249
|
-
autoflake_additional_imports: list[str] =
|
|
303
|
+
autoflake_additional_imports: list[str] = self.ingredients.imports.exportListModuleNames()
|
|
250
304
|
if self.packageName:
|
|
251
305
|
autoflake_additional_imports.append(self.packageName)
|
|
252
|
-
# TODO LedgerOfImports method: list of package names. autoflake_additional_imports.extend()
|
|
253
|
-
autoflake_additional_imports.append(getDatatypePackage())
|
|
254
306
|
pythonSource = autoflake_fix_code(pythonSource, autoflake_additional_imports, expand_star_imports=False, remove_all_unused_imports=False, remove_duplicate_keys = False, remove_unused_variables = False,)
|
|
255
|
-
self.pathFilename
|
|
256
|
-
|
|
257
|
-
@dataclasses.dataclass
|
|
258
|
-
class RecipeSynthesizeCountingFunction:
|
|
259
|
-
"""Settings for synthesizing counting functions."""
|
|
260
|
-
ingredients: IngredientsFunction
|
|
307
|
+
writeStringToHere(pythonSource, self.pathFilename)
|
|
261
308
|
|
|
262
309
|
numbaFlow: RecipeSynthesizeFlow = RecipeSynthesizeFlow()
|
|
263
310
|
|
|
264
311
|
# https://github.com/hunterhogan/mapFolding/issues/3
|
|
265
|
-
|
|
266
|
-
if
|
|
312
|
+
sourceSequentialFunctionDef = extractFunctionDef(numbaFlow.sourceSequentialCallable, numbaFlow.source_astModule)
|
|
313
|
+
if sourceSequentialFunctionDef is None: raise FREAKOUT
|
|
267
314
|
|
|
268
|
-
numbaCountSequential =
|
|
269
|
-
FunctionDef=
|
|
315
|
+
numbaCountSequential = RecipeCountingFunction(IngredientsFunction(
|
|
316
|
+
FunctionDef=sourceSequentialFunctionDef,
|
|
270
317
|
imports=LedgerOfImports(numbaFlow.source_astModule)
|
|
271
318
|
))
|
|
272
319
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
Z0Z_autoflake_additional_imports: list[str] = []
|
|
276
|
-
Z0Z_autoflake_additional_imports.append(thePackageName)
|
|
320
|
+
numbaDispatcher = RecipeModule(filenameStem=numbaFlow.moduleDispatcher, fileExtension=numbaFlow.fileExtension, pathPackage=numbaFlow.pathPackage,
|
|
321
|
+
packageName=numbaFlow.packageName, logicalPathINFIX=numbaFlow.Z0Z_flowLogicalPathRoot)
|
|
277
322
|
|
|
278
323
|
class ParametersSynthesizeNumbaCallable(NamedTuple):
|
|
279
324
|
callableTarget: str
|
|
@@ -292,6 +337,7 @@ _decoratorCallable = ''
|
|
|
292
337
|
# if numba
|
|
293
338
|
_datatypeModuleScalar = 'numba'
|
|
294
339
|
_decoratorCallable = 'jit'
|
|
340
|
+
Z0Z_autoflake_additional_imports: list[str] = []
|
|
295
341
|
Z0Z_autoflake_additional_imports.append('numba')
|
|
296
342
|
|
|
297
343
|
def Z0Z_getDatatypeModuleScalar() -> str:
|