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.
Files changed (56) hide show
  1. {mapfolding-0.7.0 → mapfolding-0.7.1}/PKG-INFO +1 -1
  2. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/__init__.py +1 -1
  3. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/basecamp.py +2 -2
  4. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/beDRY.py +13 -6
  5. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/filesystem.py +37 -29
  6. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/Z0Z_workbench.py +6 -7
  7. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeDataConverters.py +9 -9
  8. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/transformationTools.py +1 -1
  9. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/whatWillBe.py +144 -98
  10. mapfolding-0.7.0/mapFolding/theDao.py → mapfolding-0.7.1/mapFolding/syntheticModules/multiprocessingCount_doTheNeedful.py +40 -32
  11. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCountSequential.py +4 -3
  12. mapfolding-0.7.1/mapFolding/theDao.py +219 -0
  13. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/theSSOT.py +45 -30
  14. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/PKG-INFO +1 -1
  15. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/SOURCES.txt +2 -0
  16. {mapfolding-0.7.0 → mapfolding-0.7.1}/pyproject.toml +6 -5
  17. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_computations.py +9 -5
  18. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_tasks.py +9 -3
  19. {mapfolding-0.7.0 → mapfolding-0.7.1}/LICENSE +0 -0
  20. {mapfolding-0.7.0 → mapfolding-0.7.1}/README.md +0 -0
  21. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/noHomeYet.py +0 -0
  22. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/oeis.py +0 -0
  23. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/py.typed +0 -0
  24. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/flattened.py +0 -0
  25. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/hunterNumba.py +0 -0
  26. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/irvineJavaPort.py +0 -0
  27. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/jax.py +0 -0
  28. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/lunnan.py +0 -0
  29. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/lunnanNumpy.py +0 -0
  30. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/lunnanWhile.py +0 -0
  31. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/rotatedEntryPoint.py +0 -0
  32. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
  33. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/__init__.py +0 -0
  34. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -0
  35. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -0
  36. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeCountingFunctions.py +0 -0
  37. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -0
  38. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -0
  39. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -0
  40. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/__init__.py +0 -0
  41. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/dataNamespaceFlattened.py +0 -0
  42. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCount.py +0 -0
  43. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCountExample.py +0 -0
  44. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -0
  45. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numba_doTheNeedful.py +0 -0
  46. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding/syntheticModules/numba_doTheNeedfulExample.py +0 -0
  47. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/dependency_links.txt +0 -0
  48. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/entry_points.txt +0 -0
  49. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/requires.txt +0 -0
  50. {mapfolding-0.7.0 → mapfolding-0.7.1}/mapFolding.egg-info/top_level.txt +0 -0
  51. {mapfolding-0.7.0 → mapfolding-0.7.1}/setup.cfg +0 -0
  52. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/__init__.py +0 -0
  53. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/conftest.py +0 -0
  54. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_filesystem.py +0 -0
  55. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_oeis.py +0 -0
  56. {mapfolding-0.7.0 → mapfolding-0.7.1}/tests/test_other.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Count distinct ways to fold a map (or a strip of stamps)
5
5
  Author-email: Hunter Hogan <HunterHogan@pm.me>
6
6
  License: CC-BY-NC-4.0
@@ -1,4 +1,4 @@
1
- from mapFolding.basecamp import countFolds
1
+ from mapFolding.basecamp import countFolds as countFolds
2
2
  from mapFolding.oeis import clearOEIScache, getOEISids, OEIS_for_n
3
3
 
4
4
  __all__ = [
@@ -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
- dispatcher = getPackageDispatcher()
47
- computationStateComplete: ComputationState = dispatcher(computationStateInitialized)
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 Numba concurrent operations. Note that it can only affect Numba-jitted functions that have not yet been imported.
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
- concurrencyLimit = 1
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("This function only supports the 'numba' concurrency package.")
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
- dataclassIdentifierAsStr=numbaFlow.dataclassIdentifierAsStr
8
+ dataclassIdentifier=numbaFlow.sourceDataclassIdentifier
10
9
  , logicalPathModuleDataclass=numbaFlow.logicalPathModuleDataclass
11
- , dataclassInstanceAsStr=numbaFlow.dataclassInstanceAsStr
10
+ , dataclassInstance=numbaFlow.dataclassInstance
12
11
 
13
- , dispatcherCallableAsStr=numbaFlow.dispatcherCallableAsStr
12
+ , dispatcherCallable=numbaFlow.dispatcherCallable
14
13
  , logicalPathModuleDispatcher=numbaFlow.logicalPathModuleDispatcher
15
- , dataConverterCallableAsStr=numbaFlow.dataConverterCallableAsStr
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.sequentialCallableAsStr
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.moduleOfSyntheticModules,
30
+ logicalPathINFIX=numbaFlow.Z0Z_flowLogicalPathRoot,
32
31
  )
33
32
 
34
33
  ingredientsModuleDataConverter.writeModule()
@@ -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(dataclassIdentifierAsStr: str,
70
+ def makeDataclassConverter(dataclassIdentifier: str,
71
71
  logicalPathModuleDataclass: str,
72
- dataclassInstanceAsStr: str,
73
- dispatcherCallableAsStr: str,
72
+ dataclassInstance: str,
73
+ dispatcherCallable: str,
74
74
  logicalPathModuleDispatcher: str,
75
- dataConverterCallableAsStr: str,
75
+ dataConverterCallable: str,
76
76
  ) -> IngredientsFunction:
77
77
 
78
- astNameDataclass, ledgerDataclassAndFragments, list_astAnnAssign, list_astNameDataclassFragments, list_astKeywordDataclassFragments, astTupleForAssignTargetsToFragments = shatter_dataclassesDOTdataclass(logicalPathModuleDataclass, dataclassIdentifierAsStr, dataclassInstanceAsStr)
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=dataConverterCallableAsStr
82
- , argumentsSpecification=Make.astArgumentsSpecification(args=[Make.astArg(dataclassInstanceAsStr, astNameDataclass)])
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(dispatcherCallableAsStr), args=list_astNameDataclassFragments))
90
+ , value=Make.astCall(Make.astName(dispatcherCallable), args=list_astNameDataclassFragments))
91
91
  ingredientsFunction.FunctionDef.body.append(callToDispatcher)
92
- ingredientsFunction.imports.addImportFromStr(logicalPathModuleDispatcher, dispatcherCallableAsStr)
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
 
@@ -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=body, type_ignores=type_ignores)
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
- theDataclassIdentifierAsStr,
13
- theDataclassInstanceAsStr,
14
- theDispatcherCallableAsStr,
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
- Z0Z_sequentialCallableAsStr,
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: Path = thePathPackage
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
- moduleOfSyntheticModules: str = theModuleOfSyntheticModules
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
- dataclassIdentifierAsStr: str = theDataclassIdentifierAsStr
88
- dispatcherCallableAsStr: str = theDispatcherCallableAsStr
89
- dataConverterCallableAsStr: str = 'flattenData'
90
- sequentialCallableAsStr: str = Z0Z_sequentialCallableAsStr
92
+ sequentialCallable: str = sourceSequentialCallable
93
+ dataclassIdentifier: str = sourceDataclassIdentifier
94
+ dataConverterCallable: str = 'unpackDataclassPackUp'
95
+ dispatcherCallable: str = sourceDispatcherCallable
91
96
 
92
97
  # Variable
93
- dataclassInstanceAsStr: str = theDataclassInstanceAsStr
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 a module, including the package context, should be here."""
159
- name: ast_Identifier
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.name)
272
+ listModules.append(self.filenameStem)
189
273
  return '.'.join(listModules)
190
274
 
191
275
  @property
192
- def pathFilename(self) -> Path:
193
- pathRoot: Path = self.pathPackage
194
- filename = self.name + self.fileExtension
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 | None = self._getLogicalPathParent()
208
- if logicalPathParent is None:
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
- """Writes the module to disk with proper imports and functions.
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.write_text(pythonSource)
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
- sequentialFunctionDef = extractFunctionDef(numbaFlow.sequentialCallableAsStr, numbaFlow.source_astModule)
266
- if sequentialFunctionDef is None: raise FREAKOUT
312
+ sourceSequentialFunctionDef = extractFunctionDef(numbaFlow.sourceSequentialCallable, numbaFlow.source_astModule)
313
+ if sourceSequentialFunctionDef is None: raise FREAKOUT
267
314
 
268
- numbaCountSequential = RecipeSynthesizeCountingFunction(IngredientsFunction(
269
- FunctionDef=sequentialFunctionDef,
315
+ numbaCountSequential = RecipeCountingFunction(IngredientsFunction(
316
+ FunctionDef=sourceSequentialFunctionDef,
270
317
  imports=LedgerOfImports(numbaFlow.source_astModule)
271
318
  ))
272
319
 
273
- # the data converter and the dispatcher could be in the same module.
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: