mapFolding 0.8.3__tar.gz → 0.8.4__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.8.3 → mapfolding-0.8.4}/PKG-INFO +2 -1
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/__init__.py +2 -2
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/basecamp.py +11 -5
- mapfolding-0.8.4/mapFolding/filesystem.py +154 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/oeis.py +1 -1
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/someAssemblyRequired/__init__.py +37 -18
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_theTypes.py +35 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_tool_Make.py +92 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_tool_Then.py +65 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_toolboxAntecedents.py +326 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_toolboxContainers.py +306 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/_toolboxPython.py +76 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/someAssemblyRequired/ingredientsNumba.py +17 -24
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +156 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +247 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformDataStructures.py +235 -0
- mapfolding-0.8.4/mapFolding/someAssemblyRequired/transformationTools.py +156 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/syntheticModules/numbaCount_doTheNeedful.py +36 -33
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/theDao.py +13 -11
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/theSSOT.py +69 -112
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding.egg-info/PKG-INFO +2 -1
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding.egg-info/SOURCES.txt +7 -1
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding.egg-info/requires.txt +1 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/pyproject.toml +3 -2
- {mapfolding-0.8.3 → mapfolding-0.8.4}/tests/conftest.py +34 -29
- mapfolding-0.8.4/tests/test_computations.py +62 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/tests/test_filesystem.py +3 -3
- mapfolding-0.8.3/mapFolding/filesystem.py +0 -129
- mapfolding-0.8.3/mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +0 -211
- mapfolding-0.8.3/mapFolding/someAssemblyRequired/synthesizeNumbaJobVESTIGIAL.py +0 -413
- mapfolding-0.8.3/mapFolding/someAssemblyRequired/transformDataStructures.py +0 -168
- mapfolding-0.8.3/mapFolding/someAssemblyRequired/transformationTools.py +0 -778
- mapfolding-0.8.3/tests/test_computations.py +0 -53
- {mapfolding-0.8.3 → mapfolding-0.8.4}/LICENSE +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/README.md +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/beDRY.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/noHomeYet.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/py.typed +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/__init__.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/flattened.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/hunterNumba.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/irvineJavaPort.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/jaxCount.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/jobsCompleted/[2x19]/p2x19.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/jobsCompleted/__init__.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/jobsCompleted/p2x19/p2x19.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/lunnanNumpy.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/lunnanWhile.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/rotatedEntryPoint.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/reference/total_countPlus1vsPlusN.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding/syntheticModules/__init__.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding.egg-info/dependency_links.txt +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding.egg-info/entry_points.txt +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/mapFolding.egg-info/top_level.txt +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/setup.cfg +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/tests/__init__.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/tests/test_oeis.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/tests/test_other.py +0 -0
- {mapfolding-0.8.3 → mapfolding-0.8.4}/tests/test_tasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapFolding
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.4
|
|
4
4
|
Summary: Map folding algorithm with code transformation framework for optimizing numerical computations
|
|
5
5
|
Author-email: Hunter Hogan <HunterHogan@pm.me>
|
|
6
6
|
License: CC-BY-NC-4.0
|
|
@@ -45,6 +45,7 @@ Requires-Dist: pytest-cov; extra == "testing"
|
|
|
45
45
|
Requires-Dist: pytest-env; extra == "testing"
|
|
46
46
|
Requires-Dist: pytest-xdist; extra == "testing"
|
|
47
47
|
Requires-Dist: pyupgrade; extra == "testing"
|
|
48
|
+
Requires-Dist: ruff; extra == "testing"
|
|
48
49
|
Dynamic: license-file
|
|
49
50
|
|
|
50
51
|
# mapFolding: Algorithms for enumerating distinct map/stamp folding patterns 🗺️
|
|
@@ -24,13 +24,13 @@ Special directories:
|
|
|
24
24
|
core algorithm created by the code transformation framework
|
|
25
25
|
- reference/: Historical implementations and educational resources for algorithm exploration
|
|
26
26
|
- reference/jobsCompleted/: Contains successful computations for previously unknown values,
|
|
27
|
-
including first-ever calculations for
|
|
27
|
+
including first-ever calculations for 2x19 and 2x20 maps (OEIS A001415)
|
|
28
28
|
|
|
29
29
|
This package strives to balance algorithm readability and understandability with
|
|
30
30
|
high-performance computation capabilities, allowing users to compute map folding
|
|
31
31
|
totals for larger dimensions than previously feasible.
|
|
32
32
|
"""
|
|
33
|
-
from mapFolding.basecamp import countFolds
|
|
33
|
+
from mapFolding.basecamp import countFolds
|
|
34
34
|
from mapFolding.oeis import clearOEIScache, getOEISids, OEIS_for_n, oeisIDfor_n
|
|
35
35
|
|
|
36
36
|
__all__ = [
|
|
@@ -14,13 +14,13 @@ implementation, and optional persistence of results.
|
|
|
14
14
|
|
|
15
15
|
from collections.abc import Sequence
|
|
16
16
|
from mapFolding.beDRY import outfitCountFolds, setCPUlimit, validateListDimensions
|
|
17
|
-
from mapFolding.filesystem import getPathFilenameFoldsTotal, saveFoldsTotal
|
|
17
|
+
from mapFolding.filesystem import getPathFilenameFoldsTotal, saveFoldsTotal, saveFoldsTotalFAILearly
|
|
18
18
|
from mapFolding.theSSOT import ComputationState, getPackageDispatcher, The
|
|
19
19
|
from os import PathLike
|
|
20
|
-
from pathlib import
|
|
20
|
+
from pathlib import PurePath
|
|
21
21
|
|
|
22
22
|
def countFolds(listDimensions: Sequence[int]
|
|
23
|
-
, pathLikeWriteFoldsTotal:
|
|
23
|
+
, pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None
|
|
24
24
|
, computationDivisions: int | str | None = None
|
|
25
25
|
, CPUlimit: int | float | bool | None = None
|
|
26
26
|
) -> int:
|
|
@@ -57,13 +57,19 @@ def countFolds(listDimensions: Sequence[int]
|
|
|
57
57
|
concurrencyLimit: int = setCPUlimit(CPUlimit, The.concurrencyPackage)
|
|
58
58
|
computationStateInitialized: ComputationState = outfitCountFolds(mapShape, computationDivisions, concurrencyLimit)
|
|
59
59
|
|
|
60
|
+
if pathLikeWriteFoldsTotal is not None:
|
|
61
|
+
pathFilenameFoldsTotal = getPathFilenameFoldsTotal(computationStateInitialized.mapShape, pathLikeWriteFoldsTotal)
|
|
62
|
+
saveFoldsTotalFAILearly(pathFilenameFoldsTotal)
|
|
63
|
+
else:
|
|
64
|
+
pathFilenameFoldsTotal = None
|
|
65
|
+
|
|
60
66
|
dispatcherCallableProxy = getPackageDispatcher()
|
|
61
67
|
computationStateComplete: ComputationState = dispatcherCallableProxy(computationStateInitialized)
|
|
68
|
+
# computationStateComplete: ComputationState = The.dispatcher(computationStateInitialized)
|
|
62
69
|
|
|
63
70
|
computationStateComplete.getFoldsTotal()
|
|
64
71
|
|
|
65
|
-
if
|
|
66
|
-
pathFilenameFoldsTotal: Path = getPathFilenameFoldsTotal(computationStateComplete.mapShape, pathLikeWriteFoldsTotal)
|
|
72
|
+
if pathFilenameFoldsTotal is not None:
|
|
67
73
|
saveFoldsTotal(pathFilenameFoldsTotal, computationStateComplete.foldsTotal)
|
|
68
74
|
|
|
69
75
|
return computationStateComplete.foldsTotal
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Filesystem utilities for managing map folding computation results.
|
|
3
|
+
|
|
4
|
+
This module provides functions for standardized handling of files related to the mapFolding
|
|
5
|
+
package, with a focus on saving, retrieving, and naming computation results. It implements
|
|
6
|
+
consistent naming conventions and path resolution strategies to ensure that:
|
|
7
|
+
|
|
8
|
+
1. Computation results are stored in a predictable location
|
|
9
|
+
2. Filenames follow a consistent pattern based on map dimensions
|
|
10
|
+
3. Results can be reliably retrieved for future reference
|
|
11
|
+
4. The system handles file operations safely with appropriate error handling
|
|
12
|
+
|
|
13
|
+
The module serves as the interface between the computational components of the package
|
|
14
|
+
and the filesystem, abstracting away the details of file operations and path management.
|
|
15
|
+
"""
|
|
16
|
+
from mapFolding.theSSOT import The
|
|
17
|
+
from os import PathLike
|
|
18
|
+
from pathlib import Path, PurePath
|
|
19
|
+
from sys import modules as sysModules
|
|
20
|
+
from typing import Any
|
|
21
|
+
import os
|
|
22
|
+
|
|
23
|
+
def getFilenameFoldsTotal(mapShape: tuple[int, ...]) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Create a standardized filename for a computed `foldsTotal` value.
|
|
26
|
+
|
|
27
|
+
This function generates a consistent, filesystem-safe filename based on map dimensions.
|
|
28
|
+
Standardizing filenames ensures that results can be reliably stored and retrieved,
|
|
29
|
+
avoiding potential filesystem incompatibilities or Python naming restrictions.
|
|
30
|
+
|
|
31
|
+
Parameters:
|
|
32
|
+
mapShape: A sequence of integers representing the dimensions of the map.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
filenameFoldsTotal: A filename string in format 'pMxN.foldsTotal' where M,N are sorted dimensions.
|
|
36
|
+
|
|
37
|
+
Notes:
|
|
38
|
+
The filename format ensures:
|
|
39
|
+
- No spaces in the filename
|
|
40
|
+
- Safe filesystem characters
|
|
41
|
+
- Unique extension (.foldsTotal)
|
|
42
|
+
- Python-safe strings (no starting with numbers, no reserved words)
|
|
43
|
+
- The 'p' prefix preserves compatibility with Lunnan's original code.
|
|
44
|
+
"""
|
|
45
|
+
return 'p' + 'x'.join(str(dimension) for dimension in sorted(mapShape)) + '.foldsTotal'
|
|
46
|
+
|
|
47
|
+
def getPathFilenameFoldsTotal(mapShape: tuple[int, ...], pathLikeWriteFoldsTotal: PathLike[str] | PurePath | None = None) -> Path:
|
|
48
|
+
"""
|
|
49
|
+
Get a standardized path and filename for the computed foldsTotal value.
|
|
50
|
+
|
|
51
|
+
This function resolves paths for storing computation results, handling different
|
|
52
|
+
input types including directories, absolute paths, or relative paths. It ensures
|
|
53
|
+
that all parent directories exist in the resulting path.
|
|
54
|
+
|
|
55
|
+
Parameters:
|
|
56
|
+
mapShape: List of dimensions for the map folding problem.
|
|
57
|
+
pathLikeWriteFoldsTotal (getPathJobRootDEFAULT): Path, filename, or relative path and filename.
|
|
58
|
+
If None, uses default path. If a directory, appends standardized filename.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
pathFilenameFoldsTotal: Absolute path and filename for storing the foldsTotal value.
|
|
62
|
+
|
|
63
|
+
Notes:
|
|
64
|
+
The function creates any necessary directories in the path if they don't exist.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
if pathLikeWriteFoldsTotal is None:
|
|
68
|
+
pathFilenameFoldsTotal = getPathRootJobDEFAULT() / getFilenameFoldsTotal(mapShape)
|
|
69
|
+
else:
|
|
70
|
+
pathLikeSherpa = Path(pathLikeWriteFoldsTotal)
|
|
71
|
+
if pathLikeSherpa.is_dir():
|
|
72
|
+
pathFilenameFoldsTotal = pathLikeSherpa / getFilenameFoldsTotal(mapShape)
|
|
73
|
+
elif pathLikeSherpa.is_file() and pathLikeSherpa.is_absolute():
|
|
74
|
+
pathFilenameFoldsTotal = pathLikeSherpa
|
|
75
|
+
else:
|
|
76
|
+
pathFilenameFoldsTotal = getPathRootJobDEFAULT() / pathLikeSherpa
|
|
77
|
+
|
|
78
|
+
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
return pathFilenameFoldsTotal
|
|
80
|
+
|
|
81
|
+
# TODO learn how to see this from the user's perspective
|
|
82
|
+
def getPathRootJobDEFAULT() -> Path:
|
|
83
|
+
pathJobDEFAULT = The.pathPackage / "jobs"
|
|
84
|
+
if 'google.colab' in sysModules:
|
|
85
|
+
pathJobDEFAULT = Path("/content/drive/MyDrive") / "jobs"
|
|
86
|
+
pathJobDEFAULT.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
return pathJobDEFAULT
|
|
88
|
+
|
|
89
|
+
def _saveFoldsTotal(pathFilename: PathLike[str] | PurePath, foldsTotal: int) -> None:
|
|
90
|
+
pathFilenameFoldsTotal = Path(pathFilename)
|
|
91
|
+
pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
pathFilenameFoldsTotal.write_text(str(foldsTotal))
|
|
93
|
+
|
|
94
|
+
def saveFoldsTotal(pathFilename: PathLike[str] | PurePath, foldsTotal: int) -> None:
|
|
95
|
+
"""
|
|
96
|
+
Save `foldsTotal` value to disk with multiple fallback mechanisms.
|
|
97
|
+
|
|
98
|
+
This function attempts to save the computed `foldsTotal` value to the specified
|
|
99
|
+
location, with backup strategies in case the primary save attempt fails.
|
|
100
|
+
The robustness is critical since these computations may take days to complete.
|
|
101
|
+
|
|
102
|
+
Parameters:
|
|
103
|
+
pathFilename: Target save location for the `foldsTotal` value
|
|
104
|
+
foldsTotal: The computed value to save
|
|
105
|
+
|
|
106
|
+
Notes:
|
|
107
|
+
If the primary save fails, the function will attempt alternative save methods:
|
|
108
|
+
1. Print the value prominently to stdout
|
|
109
|
+
2. Create a fallback file in the current working directory
|
|
110
|
+
3. As a last resort, simply print the value
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
_saveFoldsTotal(pathFilename, foldsTotal)
|
|
114
|
+
except Exception as ERRORmessage:
|
|
115
|
+
try:
|
|
116
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
117
|
+
print(ERRORmessage)
|
|
118
|
+
print(f"\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n\n{foldsTotal=}\n\nfoldsTotal foldsTotal foldsTotal foldsTotal foldsTotal\n")
|
|
119
|
+
randomnessPlanB = (int(str(foldsTotal).strip()[-1]) + 1) * ['YO_']
|
|
120
|
+
filenameInfixUnique = ''.join(randomnessPlanB)
|
|
121
|
+
pathFilenamePlanB = os.path.join(os.getcwd(), 'foldsTotal' + filenameInfixUnique + '.txt')
|
|
122
|
+
writeStreamFallback = open(pathFilenamePlanB, 'w')
|
|
123
|
+
writeStreamFallback.write(str(foldsTotal))
|
|
124
|
+
writeStreamFallback.close()
|
|
125
|
+
print(str(pathFilenamePlanB))
|
|
126
|
+
except Exception:
|
|
127
|
+
print(foldsTotal)
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
def saveFoldsTotalFAILearly(pathFilename: PathLike[str] | PurePath) -> None:
|
|
131
|
+
if Path(pathFilename).exists():
|
|
132
|
+
raise FileExistsError(f"{pathFilename=} exists: a battle of overwriting might cause tears.")
|
|
133
|
+
if not Path(pathFilename).parent.exists():
|
|
134
|
+
raise FileNotFoundError(f"I received {pathFilename=} 0.000139 seconds ago from a function that promised it created the parent directory, but the parent directory does not exist. Fix that now, so your computation doesn't get deleted later. And be compassionate to others.")
|
|
135
|
+
foldsTotal = 149302889205120
|
|
136
|
+
_saveFoldsTotal(pathFilename, foldsTotal)
|
|
137
|
+
if not Path(pathFilename).exists():
|
|
138
|
+
raise FileNotFoundError(f"I just wrote a test file to {pathFilename=}, but it does not exist. Fix that now, so your computation doesn't get deleted later. And continually improve your empathy skills.")
|
|
139
|
+
foldsTotalRead = int(Path(pathFilename).read_text())
|
|
140
|
+
if foldsTotalRead != foldsTotal:
|
|
141
|
+
raise FileNotFoundError(f"I wrote a test file to {pathFilename=} with contents of {str(foldsTotal)=}, but I read {foldsTotalRead=} from the file. Python says the values are not equal. Fix that now, so your computation doesn't get corrupted later. And be pro-social.")
|
|
142
|
+
|
|
143
|
+
def writeStringToHere(this: str, pathFilename: PathLike[str] | PurePath) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Write a string to a file, creating parent directories if needed.
|
|
146
|
+
|
|
147
|
+
Parameters:
|
|
148
|
+
this: The string content to write to the file
|
|
149
|
+
pathFilename: The target file path where the string should be written
|
|
150
|
+
"""
|
|
151
|
+
pathFilename = Path(pathFilename)
|
|
152
|
+
pathFilename.parent.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
pathFilename.write_text(str(this))
|
|
154
|
+
return None
|
|
@@ -24,23 +24,42 @@ These tools were developed for map folding computation optimization but are desi
|
|
|
24
24
|
general-purpose utilities applicable to a wide range of code transformation scenarios,
|
|
25
25
|
particularly for numerically-intensive algorithms that benefit from just-in-time compilation.
|
|
26
26
|
"""
|
|
27
|
+
from mapFolding.someAssemblyRequired._theTypes import (
|
|
28
|
+
ast_expr_Slice,
|
|
29
|
+
ast_Identifier,
|
|
30
|
+
ImaAnnotationType,
|
|
31
|
+
astClassHasDOTnameNotName,
|
|
32
|
+
astClassHasDOTnameNotNameOptional,
|
|
33
|
+
astClassHasDOTtarget,
|
|
34
|
+
astClassHasDOTvalue,
|
|
35
|
+
astMosDef,
|
|
36
|
+
intORlist_ast_type_paramORstr_orNone,
|
|
37
|
+
intORstr_orNone,
|
|
38
|
+
list_ast_type_paramORstr_orNone,
|
|
39
|
+
str_nameDOTname,
|
|
40
|
+
typeCertified,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
from mapFolding.someAssemblyRequired._toolboxPython import (
|
|
44
|
+
importLogicalPath2Callable,
|
|
45
|
+
importPathFilename2Callable,
|
|
46
|
+
NodeChanger,
|
|
47
|
+
NodeTourist,
|
|
48
|
+
parseLogicalPath2astModule,
|
|
49
|
+
parsePathFilename2astModule,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
from mapFolding.someAssemblyRequired._toolboxAntecedents import be, ifThis, 又
|
|
53
|
+
from mapFolding.someAssemblyRequired._tool_Make import Make
|
|
54
|
+
from mapFolding.someAssemblyRequired._tool_Then import Then
|
|
55
|
+
|
|
27
56
|
from mapFolding.someAssemblyRequired.transformationTools import (
|
|
28
|
-
|
|
29
|
-
extractClassDef
|
|
30
|
-
extractFunctionDef
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Make as Make,
|
|
37
|
-
makeDictionaryReplacementStatements as makeDictionaryReplacementStatements,
|
|
38
|
-
NodeCollector as NodeCollector,
|
|
39
|
-
NodeReplacer as NodeReplacer,
|
|
40
|
-
RecipeSynthesizeFlow as RecipeSynthesizeFlow,
|
|
41
|
-
strDotStrCuzPyStoopid as strDotStrCuzPyStoopid,
|
|
42
|
-
Then as Then,
|
|
43
|
-
write_astModule as write_astModule,
|
|
44
|
-
Z0Z_executeActionUnlessDescendantMatches as Z0Z_executeActionUnlessDescendantMatches,
|
|
45
|
-
Z0Z_replaceMatchingASTnodes as Z0Z_replaceMatchingASTnodes,
|
|
57
|
+
dictionaryEstimates,
|
|
58
|
+
extractClassDef,
|
|
59
|
+
extractFunctionDef,
|
|
60
|
+
write_astModule,
|
|
61
|
+
Z0Z_executeActionUnlessDescendantMatches,
|
|
62
|
+
Z0Z_inlineThisFunctionWithTheseValues,
|
|
63
|
+
Z0Z_lameFindReplace,
|
|
64
|
+
Z0Z_makeDictionaryReplacementStatements,
|
|
46
65
|
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type definitions used across the AST transformation modules.
|
|
3
|
+
|
|
4
|
+
This module provides type aliases and variables used in AST manipulation,
|
|
5
|
+
centralizing type definitions to prevent circular imports.
|
|
6
|
+
"""
|
|
7
|
+
from typing import Any, TYPE_CHECKING, TypeAlias as typing_TypeAlias, TypeVar as typing_TypeVar
|
|
8
|
+
import ast
|
|
9
|
+
|
|
10
|
+
stuPyd: typing_TypeAlias = str
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
astClassHasDOTnameNotName: typing_TypeAlias = ast.alias | ast.AsyncFunctionDef | ast.ClassDef | ast.FunctionDef | ast.ParamSpec | ast.TypeVar | ast.TypeVarTuple
|
|
14
|
+
astClassHasDOTnameNotNameOptional: typing_TypeAlias = astClassHasDOTnameNotName | ast.ExceptHandler | ast.MatchAs | ast.MatchStar | None
|
|
15
|
+
astClassHasDOTtarget: typing_TypeAlias = ast.AnnAssign | ast.AsyncFor | ast.AugAssign | ast.comprehension | ast.For | ast.NamedExpr
|
|
16
|
+
astClassHasDOTvalue: typing_TypeAlias = ast.AnnAssign | ast.Assign | ast.Attribute | ast.AugAssign | ast.Await | ast.Constant | ast.DictComp | ast.Expr | ast.FormattedValue | ast.keyword | ast.MatchValue | ast.NamedExpr | ast.Return | ast.Starred | ast.Subscript | ast.TypeAlias | ast.Yield | ast.YieldFrom
|
|
17
|
+
else:
|
|
18
|
+
astClassHasDOTnameNotName = stuPyd
|
|
19
|
+
astClassHasDOTnameNotNameOptional = stuPyd
|
|
20
|
+
astClassHasDOTtarget = stuPyd
|
|
21
|
+
astClassHasDOTvalue = stuPyd
|
|
22
|
+
|
|
23
|
+
ast_expr_Slice: typing_TypeAlias = ast.expr
|
|
24
|
+
ast_Identifier: typing_TypeAlias = str
|
|
25
|
+
intORlist_ast_type_paramORstr_orNone: typing_TypeAlias = Any
|
|
26
|
+
intORstr_orNone: typing_TypeAlias = Any
|
|
27
|
+
list_ast_type_paramORstr_orNone: typing_TypeAlias = Any
|
|
28
|
+
# TODO I am using the moniker `nameDOTname` in two very different ways: differentiate them.
|
|
29
|
+
str_nameDOTname: typing_TypeAlias = stuPyd
|
|
30
|
+
ImaAnnotationType: typing_TypeAlias = ast.Attribute | ast.Constant | ast.Name | ast.Subscript
|
|
31
|
+
|
|
32
|
+
# TODO understand whatever the fuck `typing.TypeVar` is _supposed_ to fucking do.
|
|
33
|
+
typeCertified = typing_TypeVar('typeCertified')
|
|
34
|
+
|
|
35
|
+
astMosDef = typing_TypeVar('astMosDef', bound=astClassHasDOTnameNotName)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from mapFolding.someAssemblyRequired import ast_expr_Slice, ast_Identifier, ImaAnnotationType, intORlist_ast_type_paramORstr_orNone, intORstr_orNone, list_ast_type_paramORstr_orNone
|
|
3
|
+
from typing import Any
|
|
4
|
+
import ast
|
|
5
|
+
|
|
6
|
+
class Make:
|
|
7
|
+
"""Almost all parameters described here are only accessible through a method's `**keywordArguments` parameter.
|
|
8
|
+
|
|
9
|
+
Parameters:
|
|
10
|
+
context (ast.Load()): Are you loading from, storing to, or deleting the identifier? The `context` (also, `ctx`) value is `ast.Load()`, `ast.Store()`, or `ast.Del()`.
|
|
11
|
+
col_offset (0): int Position information specifying the column where an AST node begins.
|
|
12
|
+
end_col_offset (None): int|None Position information specifying the column where an AST node ends.
|
|
13
|
+
end_lineno (None): int|None Position information specifying the line number where an AST node ends.
|
|
14
|
+
level (0): int Module import depth level that controls relative vs absolute imports. Default 0 indicates absolute import.
|
|
15
|
+
lineno: int Position information manually specifying the line number where an AST node begins.
|
|
16
|
+
kind (None): str|None Used for type annotations in limited cases.
|
|
17
|
+
type_comment (None): str|None "type_comment is an optional string with the type annotation as a comment." or `# type: ignore`.
|
|
18
|
+
type_params: list[ast.type_param] Type parameters for generic type definitions.
|
|
19
|
+
|
|
20
|
+
The `ast._Attributes`, lineno, col_offset, end_lineno, and end_col_offset, hold position information; however, they are, importantly, _not_ `ast._fields`.
|
|
21
|
+
"""
|
|
22
|
+
@staticmethod
|
|
23
|
+
def alias(name: ast_Identifier, asname: ast_Identifier | None = None) -> ast.alias:
|
|
24
|
+
return ast.alias(name, asname)
|
|
25
|
+
@staticmethod
|
|
26
|
+
def AnnAssign(target: ast.Attribute | ast.Name | ast.Subscript, annotation: ImaAnnotationType, value: ast.expr | None = None, **keywordArguments: int) -> ast.AnnAssign: # `simple: int`: uses a clever int-from-boolean to assign the correct value to the `simple` attribute. So, don't make it a method parameter.
|
|
27
|
+
return ast.AnnAssign(target, annotation, value, simple=int(isinstance(target, ast.Name)), **keywordArguments)
|
|
28
|
+
@staticmethod
|
|
29
|
+
def arg(identifier: ast_Identifier, annotation: ast.expr | None = None, **keywordArguments: intORstr_orNone) -> ast.arg:
|
|
30
|
+
return ast.arg(identifier, annotation, **keywordArguments)
|
|
31
|
+
@staticmethod
|
|
32
|
+
def argumentsSpecification(posonlyargs:list[ast.arg]=[], args:list[ast.arg]=[], vararg:ast.arg|None=None, kwonlyargs:list[ast.arg]=[], kw_defaults:list[ast.expr|None]=[None], kwarg:ast.arg|None=None, defaults:list[ast.expr]=[]) -> ast.arguments:
|
|
33
|
+
return ast.arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults)
|
|
34
|
+
@staticmethod
|
|
35
|
+
def Assign(listTargets: Any, value: ast.expr, **keywordArguments: intORstr_orNone) -> ast.Assign:
|
|
36
|
+
return ast.Assign(listTargets, value, **keywordArguments)
|
|
37
|
+
@staticmethod
|
|
38
|
+
def Attribute(value: ast.expr, *attribute: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Attribute:
|
|
39
|
+
""" If two `ast_Identifier` are joined by a dot `.`, they are _usually_ an `ast.Attribute`, but see `ast.ImportFrom`.
|
|
40
|
+
Parameters:
|
|
41
|
+
value: the part before the dot (Often `ast.Name`, but also `ast.Attribute`, `ast.Starred`, and `ast.Subscript`.)
|
|
42
|
+
attribute: an `ast_Identifier` after a dot `.`; you can pass multiple `attribute` and they will be chained together.
|
|
43
|
+
"""
|
|
44
|
+
# TODO confirm the precision of the docstring.
|
|
45
|
+
def addDOTattribute(chain, identifier: ast_Identifier, context: ast.expr_context, **keywordArguments: int) -> ast.Attribute:
|
|
46
|
+
return ast.Attribute(value=chain, attr=identifier, ctx=context, **keywordArguments)
|
|
47
|
+
buffaloBuffalo = addDOTattribute(value, attribute[0], context, **keywordArguments)
|
|
48
|
+
for identifier in attribute[1:None]:
|
|
49
|
+
buffaloBuffalo = addDOTattribute(buffaloBuffalo, identifier, context, **keywordArguments)
|
|
50
|
+
return buffaloBuffalo
|
|
51
|
+
@staticmethod
|
|
52
|
+
# TODO are the types for `callee` comprehensive?
|
|
53
|
+
# TODO is there an easier way to create precise typings for `ast`? I mean, it's a fucking closed system: there should be a lot of mystery involved.
|
|
54
|
+
def Call(callee: ast.Attribute | ast.Name | ast.Subscript, listArguments: Sequence[ast.expr] | None = None, list_astKeywords: Sequence[ast.keyword] | None = None) -> ast.Call:
|
|
55
|
+
return ast.Call(func=callee, args=list(listArguments) if listArguments else [], keywords=list(list_astKeywords) if list_astKeywords else [])
|
|
56
|
+
@staticmethod
|
|
57
|
+
def ClassDef(name: ast_Identifier, listBases: list[ast.expr]=[], list_keyword: list[ast.keyword]=[], body: list[ast.stmt]=[], decorator_list: list[ast.expr]=[], **keywordArguments: list_ast_type_paramORstr_orNone) -> ast.ClassDef:
|
|
58
|
+
return ast.ClassDef(name, listBases, list_keyword, body, decorator_list, **keywordArguments)
|
|
59
|
+
@staticmethod
|
|
60
|
+
def Constant(value: Any, **keywordArguments: intORstr_orNone) -> ast.Constant:
|
|
61
|
+
"""value: str|int|float|bool|None|bytes|bytearray|memoryview|complex|list|tuple|dict|set, or any other type that can be represented as a constant in Python."""
|
|
62
|
+
return ast.Constant(value, **keywordArguments)
|
|
63
|
+
@staticmethod
|
|
64
|
+
def Expr(value: ast.expr, **keywordArguments: int) -> ast.Expr:
|
|
65
|
+
return ast.Expr(value, **keywordArguments)
|
|
66
|
+
@staticmethod
|
|
67
|
+
def FunctionDef(name: ast_Identifier, argumentsSpecification:ast.arguments=ast.arguments(), body:list[ast.stmt]=[], decorator_list:list[ast.expr]=[], returns:ast.expr|None=None, **keywordArguments: intORlist_ast_type_paramORstr_orNone) -> ast.FunctionDef:
|
|
68
|
+
return ast.FunctionDef(name, argumentsSpecification, body, decorator_list, returns, **keywordArguments)
|
|
69
|
+
@staticmethod
|
|
70
|
+
def Import(moduleIdentifier: ast_Identifier, asname: ast_Identifier | None = None, **keywordArguments: int) -> ast.Import:
|
|
71
|
+
return ast.Import(names=[Make.alias(moduleIdentifier, asname)], **keywordArguments)
|
|
72
|
+
@staticmethod
|
|
73
|
+
def ImportFrom(moduleIdentifier: ast_Identifier, list_astAlias: list[ast.alias], **keywordArguments: int) -> ast.ImportFrom:
|
|
74
|
+
return ast.ImportFrom(moduleIdentifier, list_astAlias, **keywordArguments)
|
|
75
|
+
@staticmethod
|
|
76
|
+
def keyword(keywordArgument: ast_Identifier, value: ast.expr, **keywordArguments: int) -> ast.keyword:
|
|
77
|
+
return ast.keyword(arg=keywordArgument, value=value, **keywordArguments)
|
|
78
|
+
@staticmethod
|
|
79
|
+
def Module(body: list[ast.stmt] = [], type_ignores: list[ast.TypeIgnore] = []) -> ast.Module:
|
|
80
|
+
return ast.Module(body, type_ignores)
|
|
81
|
+
@staticmethod
|
|
82
|
+
def Name(identifier: ast_Identifier, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Name:
|
|
83
|
+
return ast.Name(identifier, context, **keywordArguments)
|
|
84
|
+
@staticmethod
|
|
85
|
+
def Return(value: ast.expr | None = None, **keywordArguments: int) -> ast.Return:
|
|
86
|
+
return ast.Return(value, **keywordArguments)
|
|
87
|
+
@staticmethod
|
|
88
|
+
def Subscript(value: ast.expr, slice: ast_expr_Slice, context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Subscript:
|
|
89
|
+
return ast.Subscript(value, slice, context, **keywordArguments)
|
|
90
|
+
@staticmethod
|
|
91
|
+
def Tuple(elements: Sequence[ast.expr] = [], context: ast.expr_context = ast.Load(), **keywordArguments: int) -> ast.Tuple:
|
|
92
|
+
return ast.Tuple(list(elements), context, **keywordArguments)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from collections.abc import Callable, Sequence
|
|
2
|
+
from mapFolding.someAssemblyRequired import ast_Identifier, astClassHasDOTvalue, astMosDef
|
|
3
|
+
from typing import Any
|
|
4
|
+
import ast
|
|
5
|
+
|
|
6
|
+
class Then:
|
|
7
|
+
@staticmethod
|
|
8
|
+
def allOf(listActions: Sequence[Callable[[ast.AST], Any]]) -> Callable[[ast.AST], ast.AST]:
|
|
9
|
+
def workhorse(node: ast.AST) -> ast.AST:
|
|
10
|
+
for action in listActions: action(node)
|
|
11
|
+
return node
|
|
12
|
+
return workhorse
|
|
13
|
+
|
|
14
|
+
@staticmethod
|
|
15
|
+
def appendTo(listOfAny: list[Any]) -> Callable[[ast.AST], None]:
|
|
16
|
+
return lambda node: listOfAny.append(node)
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def DOTarg(action: Callable[[Any], Any]) -> Callable[[ast.arg | ast.keyword], ast.arg | ast.keyword]:
|
|
20
|
+
def workhorse(node: ast.arg | ast.keyword) -> ast.arg | ast.keyword:
|
|
21
|
+
node.arg = action(node.arg)
|
|
22
|
+
return node
|
|
23
|
+
return workhorse
|
|
24
|
+
@staticmethod
|
|
25
|
+
def DOTfunc(action: Callable[[Any], Any]) -> Callable[[ast.Call], ast.Call]:
|
|
26
|
+
def workhorse(node: ast.Call) -> ast.Call:
|
|
27
|
+
node.func = action(node.func)
|
|
28
|
+
return node
|
|
29
|
+
return workhorse
|
|
30
|
+
@staticmethod
|
|
31
|
+
def DOTid(action: Callable[[Any], Any]) -> Callable[[ast.Name], ast.Name]:
|
|
32
|
+
def workhorse(node: ast.Name) -> ast.Name:
|
|
33
|
+
node.id = action(node.id)
|
|
34
|
+
return node
|
|
35
|
+
return workhorse
|
|
36
|
+
@staticmethod
|
|
37
|
+
def DOTtarget(action: Callable[[Any], Any]) -> Callable[[ast.AnnAssign | ast.AugAssign], ast.AnnAssign | ast.AugAssign]:
|
|
38
|
+
def workhorse(node: ast.AnnAssign | ast.AugAssign) -> ast.AnnAssign | ast.AugAssign:
|
|
39
|
+
node.target = action(node.target)
|
|
40
|
+
return node
|
|
41
|
+
return workhorse
|
|
42
|
+
@staticmethod
|
|
43
|
+
def DOTvalue(action: Callable[[Any], Any]) -> Callable[[astClassHasDOTvalue], astClassHasDOTvalue]:
|
|
44
|
+
def workhorse(node: astClassHasDOTvalue) -> astClassHasDOTvalue:
|
|
45
|
+
node.value = action(node.value)
|
|
46
|
+
return node
|
|
47
|
+
return workhorse
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def getIt(node: ast.AST) -> ast.AST | ast_Identifier:
|
|
51
|
+
return node
|
|
52
|
+
@staticmethod
|
|
53
|
+
def insertThisAbove(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
|
|
54
|
+
return lambda aboveMe: [*list_astAST, aboveMe]
|
|
55
|
+
@staticmethod
|
|
56
|
+
def insertThisBelow(list_astAST: Sequence[ast.AST]) -> Callable[[ast.AST], Sequence[ast.AST]]:
|
|
57
|
+
return lambda belowMe: [belowMe, *list_astAST]
|
|
58
|
+
@staticmethod
|
|
59
|
+
def removeIt(_node: ast.AST) -> None: return None
|
|
60
|
+
@staticmethod
|
|
61
|
+
def replaceWith(astAST: ast.AST | ast_Identifier) -> Callable[[ast.AST], ast.AST | ast_Identifier]:
|
|
62
|
+
return lambda _replaceMe: astAST
|
|
63
|
+
@staticmethod
|
|
64
|
+
def updateThis(dictionaryOf_astMosDef: dict[ast_Identifier, astMosDef]) -> Callable[[astMosDef], astMosDef]:
|
|
65
|
+
return lambda node: dictionaryOf_astMosDef.setdefault(node.name, node)
|