mapFolding 0.8.4__py3-none-any.whl → 0.8.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. mapFolding/__init__.py +10 -6
  2. mapFolding/basecamp.py +3 -3
  3. mapFolding/beDRY.py +241 -68
  4. mapFolding/oeis.py +41 -26
  5. mapFolding/reference/hunterNumba.py +1 -1
  6. mapFolding/someAssemblyRequired/__init__.py +16 -15
  7. mapFolding/someAssemblyRequired/_theTypes.py +31 -13
  8. mapFolding/someAssemblyRequired/_tool_Make.py +13 -5
  9. mapFolding/someAssemblyRequired/_tool_Then.py +12 -5
  10. mapFolding/someAssemblyRequired/_toolboxAntecedents.py +131 -99
  11. mapFolding/someAssemblyRequired/_toolboxContainers.py +92 -15
  12. mapFolding/someAssemblyRequired/_toolboxPython.py +17 -31
  13. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +2 -2
  14. mapFolding/someAssemblyRequired/newInliner.py +22 -0
  15. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +65 -116
  16. mapFolding/someAssemblyRequired/toolboxNumba.py +364 -0
  17. mapFolding/someAssemblyRequired/transformationTools.py +262 -41
  18. mapFolding/syntheticModules/numbaCount_doTheNeedful.py +0 -1
  19. mapFolding/theSSOT.py +30 -33
  20. mapFolding/{filesystem.py → toolboxFilesystem.py} +90 -25
  21. {mapfolding-0.8.4.dist-info → mapfolding-0.8.6.dist-info}/METADATA +3 -2
  22. mapfolding-0.8.6.dist-info/RECORD +47 -0
  23. tests/conftest.py +30 -31
  24. tests/test_computations.py +8 -7
  25. tests/test_filesystem.py +2 -2
  26. tests/test_other.py +2 -2
  27. tests/test_tasks.py +3 -3
  28. mapFolding/noHomeYet.py +0 -32
  29. mapFolding/someAssemblyRequired/ingredientsNumba.py +0 -199
  30. mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py +0 -156
  31. mapFolding/someAssemblyRequired/transformDataStructures.py +0 -235
  32. mapfolding-0.8.4.dist-info/RECORD +0 -49
  33. {mapfolding-0.8.4.dist-info → mapfolding-0.8.6.dist-info}/WHEEL +0 -0
  34. {mapfolding-0.8.4.dist-info → mapfolding-0.8.6.dist-info}/entry_points.txt +0 -0
  35. {mapfolding-0.8.4.dist-info → mapfolding-0.8.6.dist-info}/licenses/LICENSE +0 -0
  36. {mapfolding-0.8.4.dist-info → mapfolding-0.8.6.dist-info}/top_level.txt +0 -0
@@ -1,199 +0,0 @@
1
- """
2
- Numba-specific ingredients for optimized code generation.
3
-
4
- This module provides specialized tools, constants, and types specifically designed
5
- for transforming Python code into Numba-accelerated implementations. It implements:
6
-
7
- 1. A range of Numba jit decorator configurations for different optimization scenarios
8
- 2. Functions to identify and manipulate Numba decorators in abstract syntax trees
9
- 3. Utilities for applying appropriate Numba typing to transformed code
10
- 4. Parameter management for Numba compilation options
11
-
12
- The configurations range from conservative options that prioritize compatibility and
13
- error detection to aggressive optimizations that maximize performance at the cost of
14
- flexibility. While this module specifically targets Numba, its design follows the pattern
15
- of generic code transformation tools in the package, allowing similar approaches to be
16
- applied to other acceleration technologies.
17
-
18
- This module works in conjunction with transformation tools to convert the general-purpose
19
- algorithm implementation into a highly-optimized Numba version.
20
- """
21
-
22
- from collections.abc import Callable, Sequence
23
- from mapFolding.someAssemblyRequired import Make
24
- from mapFolding.someAssemblyRequired._toolboxContainers import IngredientsFunction
25
- from numba.core.compiler import CompilerBase as numbaCompilerBase
26
- from typing import Any, cast, Final, TYPE_CHECKING
27
- import ast
28
-
29
- try:
30
- from typing import NotRequired
31
- except Exception:
32
- from typing_extensions import NotRequired
33
-
34
- if TYPE_CHECKING:
35
- from typing import TypedDict
36
- else:
37
- TypedDict = dict
38
-
39
- class ParametersNumba(TypedDict):
40
- _dbg_extend_lifetimes: NotRequired[bool]
41
- _dbg_optnone: NotRequired[bool]
42
- _nrt: NotRequired[bool]
43
- boundscheck: NotRequired[bool]
44
- cache: bool
45
- debug: NotRequired[bool]
46
- error_model: str
47
- fastmath: bool
48
- forceinline: bool
49
- forceobj: NotRequired[bool]
50
- inline: str
51
- locals: NotRequired[dict[str, Any]]
52
- looplift: bool
53
- no_cfunc_wrapper: bool
54
- no_cpython_wrapper: bool
55
- no_rewrites: NotRequired[bool]
56
- nogil: NotRequired[bool]
57
- nopython: bool
58
- parallel: bool
59
- pipeline_class: NotRequired[type[numbaCompilerBase]]
60
- signature_or_function: NotRequired[Any | Callable[..., Any] | str | tuple[Any, ...]]
61
- target: NotRequired[str]
62
-
63
- parametersNumbaFailEarly: Final[ParametersNumba] = {
64
- '_nrt': True,
65
- 'boundscheck': True,
66
- 'cache': True,
67
- 'error_model': 'python',
68
- 'fastmath': False,
69
- 'forceinline': True,
70
- 'inline': 'always',
71
- 'looplift': False,
72
- 'no_cfunc_wrapper': False,
73
- 'no_cpython_wrapper': False,
74
- 'nopython': True,
75
- 'parallel': False,
76
- }
77
- """For a production function: speed is irrelevant, error discovery is paramount, must be compatible with anything downstream."""
78
-
79
- parametersNumbaDefault: Final[ParametersNumba] = {
80
- '_nrt': True,
81
- 'boundscheck': False,
82
- 'cache': True,
83
- 'error_model': 'numpy',
84
- 'fastmath': True,
85
- 'forceinline': True,
86
- 'inline': 'always',
87
- 'looplift': False,
88
- 'no_cfunc_wrapper': False,
89
- 'no_cpython_wrapper': False,
90
- 'nopython': True,
91
- 'parallel': False, }
92
- """Middle of the road: fast, lean, but will talk to non-jitted functions."""
93
-
94
- parametersNumbaParallelDEFAULT: Final[ParametersNumba] = {
95
- **parametersNumbaDefault,
96
- '_nrt': True,
97
- 'parallel': True, }
98
- """Middle of the road: fast, lean, but will talk to non-jitted functions."""
99
-
100
- parametersNumbaSuperJit: Final[ParametersNumba] = {
101
- **parametersNumbaDefault,
102
- 'no_cfunc_wrapper': True,
103
- 'no_cpython_wrapper': True, }
104
- """Speed, no helmet, no talking to non-jitted functions."""
105
-
106
- parametersNumbaSuperJitParallel: Final[ParametersNumba] = {
107
- **parametersNumbaSuperJit,
108
- '_nrt': True,
109
- 'parallel': True, }
110
- """Speed, no helmet, concurrency, no talking to non-jitted functions."""
111
-
112
- parametersNumbaMinimum: Final[ParametersNumba] = {
113
- '_nrt': True,
114
- 'boundscheck': True,
115
- 'cache': True,
116
- 'error_model': 'numpy',
117
- 'fastmath': True,
118
- 'forceinline': False,
119
- 'inline': 'always',
120
- 'looplift': False,
121
- 'no_cfunc_wrapper': False,
122
- 'no_cpython_wrapper': False,
123
- 'nopython': False,
124
- 'forceobj': True,
125
- 'parallel': False, }
126
-
127
- Z0Z_numbaDataTypeModule = 'numba'
128
- Z0Z_decoratorCallable = 'jit'
129
-
130
- def decorateCallableWithNumba(ingredientsFunction: IngredientsFunction, parametersNumba: ParametersNumba | None = None) -> IngredientsFunction:
131
- def Z0Z_UnhandledDecorators(astCallable: ast.FunctionDef) -> ast.FunctionDef:
132
- # TODO: more explicit handling of decorators. I'm able to ignore this because I know `algorithmSource` doesn't have any decorators.
133
- for decoratorItem in astCallable.decorator_list.copy():
134
- import warnings
135
- astCallable.decorator_list.remove(decoratorItem)
136
- warnings.warn(f"Removed decorator {ast.unparse(decoratorItem)} from {astCallable.name}")
137
- return astCallable
138
-
139
- def makeSpecialSignatureForNumba(signatureElement: ast.arg) -> ast.Subscript | ast.Name | None: # type: ignore
140
- if isinstance(signatureElement.annotation, ast.Subscript) and isinstance(signatureElement.annotation.slice, ast.Tuple):
141
- annotationShape: ast.expr = signatureElement.annotation.slice.elts[0]
142
- if isinstance(annotationShape, ast.Subscript) and isinstance(annotationShape.slice, ast.Tuple):
143
- shapeAsListSlices: list[ast.Slice] = [ast.Slice() for _axis in range(len(annotationShape.slice.elts))]
144
- shapeAsListSlices[-1] = ast.Slice(step=ast.Constant(value=1))
145
- shapeAST: ast.Slice | ast.Tuple = ast.Tuple(elts=list(shapeAsListSlices), ctx=ast.Load())
146
- else:
147
- shapeAST = ast.Slice(step=ast.Constant(value=1))
148
-
149
- annotationDtype: ast.expr = signatureElement.annotation.slice.elts[1]
150
- if (isinstance(annotationDtype, ast.Subscript) and isinstance(annotationDtype.slice, ast.Attribute)):
151
- datatypeAST = annotationDtype.slice.attr
152
- else:
153
- datatypeAST = None
154
-
155
- ndarrayName = signatureElement.arg
156
- Z0Z_hacky_dtype: str = ndarrayName
157
- datatype_attr = datatypeAST or Z0Z_hacky_dtype
158
- ingredientsFunction.imports.addImportFrom_asStr(datatypeModuleDecorator, datatype_attr)
159
- datatypeNumba = ast.Name(id=datatype_attr, ctx=ast.Load())
160
-
161
- return ast.Subscript(value=datatypeNumba, slice=shapeAST, ctx=ast.Load())
162
-
163
- elif isinstance(signatureElement.annotation, ast.Name):
164
- return signatureElement.annotation
165
- return None
166
-
167
- datatypeModuleDecorator: str = Z0Z_numbaDataTypeModule
168
- list_argsDecorator: Sequence[ast.expr] = []
169
-
170
- list_arg4signature_or_function: list[ast.expr] = []
171
- for parameter in ingredientsFunction.astFunctionDef.args.args:
172
- # Efficient translation of Python scalar types to Numba types https://github.com/hunterhogan/mapFolding/issues/8
173
- # For now, let Numba infer them.
174
- continue
175
- # signatureElement: ast.Subscript | ast.Name | None = makeSpecialSignatureForNumba(parameter)
176
- # if signatureElement:
177
- # list_arg4signature_or_function.append(signatureElement)
178
-
179
- if ingredientsFunction.astFunctionDef.returns and isinstance(ingredientsFunction.astFunctionDef.returns, ast.Name):
180
- theReturn: ast.Name = ingredientsFunction.astFunctionDef.returns
181
- list_argsDecorator = [cast(ast.expr, ast.Call(func=ast.Name(id=theReturn.id, ctx=ast.Load())
182
- , args=list_arg4signature_or_function if list_arg4signature_or_function else [], keywords=[] ) )]
183
- elif list_arg4signature_or_function:
184
- list_argsDecorator = [cast(ast.expr, ast.Tuple(elts=list_arg4signature_or_function, ctx=ast.Load()))]
185
-
186
- ingredientsFunction.astFunctionDef = Z0Z_UnhandledDecorators(ingredientsFunction.astFunctionDef)
187
- if parametersNumba is None:
188
- parametersNumba = parametersNumbaDefault
189
- listDecoratorKeywords: list[ast.keyword] = [Make.keyword(parameterName, Make.Constant(parameterValue)) for parameterName, parameterValue in parametersNumba.items()]
190
-
191
- decoratorModule: str = Z0Z_numbaDataTypeModule
192
- decoratorCallable: str = Z0Z_decoratorCallable
193
- ingredientsFunction.imports.addImportFrom_asStr(decoratorModule, decoratorCallable)
194
- # Leave this line in so that global edits will change it.
195
- astDecorator: ast.Call = Make.Call(Make.Name(decoratorCallable), list_argsDecorator, listDecoratorKeywords)
196
- astDecorator: ast.Call = Make.Call(Make.Name(decoratorCallable), list_astKeywords=listDecoratorKeywords)
197
-
198
- ingredientsFunction.astFunctionDef.decorator_list = [astDecorator]
199
- return ingredientsFunction
@@ -1,156 +0,0 @@
1
- """
2
- Orchestrator for generating Numba-optimized versions of the map folding algorithm.
3
-
4
- This module transforms the pure Python implementation of the map folding algorithm
5
- into a highly-optimized Numba implementation. It serves as the high-level coordinator
6
- for the code transformation process, orchestrating the following steps:
7
-
8
- 1. Extracting the core algorithm functions from the source implementation
9
- 2. Transforming function signatures and state handling for Numba compatibility
10
- 3. Converting state-based operations to direct primitive operations
11
- 4. Applying Numba decorators with appropriate optimization parameters
12
- 5. Managing imports and dependencies for the generated code
13
- 6. Assembling and writing the transformed implementation
14
-
15
- The transformation process preserves the algorithm's logic while dramatically improving
16
- performance by leveraging Numba's just-in-time compilation capabilities. This module
17
- depends on the abstract transformation tools, dataclass handling utilities, and
18
- Numba-specific optimization configurations from other modules in the package.
19
-
20
- The primary entry point is the makeNumbaFlow function, which can be executed directly
21
- to generate a fresh optimized implementation.
22
- """
23
-
24
- from mapFolding.someAssemblyRequired import (
25
- be,
26
- ifThis,
27
- Make,
28
- NodeChanger,
29
- NodeTourist,
30
- Then,
31
- Z0Z_inlineThisFunctionWithTheseValues,
32
- Z0Z_lameFindReplace,
33
- Z0Z_makeDictionaryReplacementStatements,
34
- 又,
35
- )
36
- from mapFolding.someAssemblyRequired._toolboxContainers import (
37
- astModuleToIngredientsFunction,
38
- IngredientsModule,
39
- LedgerOfImports,
40
- RecipeSynthesizeFlow,
41
- )
42
- from mapFolding.someAssemblyRequired.ingredientsNumba import decorateCallableWithNumba
43
- from mapFolding.someAssemblyRequired.transformDataStructures import shatter_dataclassesDOTdataclass
44
- from mapFolding.someAssemblyRequired.transformationTools import write_astModule
45
- import ast
46
-
47
- def makeNumbaFlow(numbaFlow: RecipeSynthesizeFlow) -> None:
48
- # TODO a tool to automatically remove unused variables from the ArgumentsSpecification (return, and returns) _might_ be nice.
49
- # Figure out dynamic flow control to synthesized modules https://github.com/hunterhogan/mapFolding/issues/4
50
-
51
- listAllIngredientsFunctions = [
52
- (ingredientsInitialize := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableInitialize)),
53
- (ingredientsParallel := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableParallel)),
54
- (ingredientsSequential := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableSequential)),
55
- (ingredientsDispatcher := astModuleToIngredientsFunction(numbaFlow.source_astModule, numbaFlow.sourceCallableDispatcher)),
56
- ]
57
-
58
- # Inline functions ========================================================
59
- dictionaryReplacementStatements = Z0Z_makeDictionaryReplacementStatements(numbaFlow.source_astModule)
60
- # NOTE Replacements statements are based on the identifiers in the _source_, so operate on the source identifiers.
61
- ingredientsInitialize.astFunctionDef = Z0Z_inlineThisFunctionWithTheseValues(ingredientsInitialize.astFunctionDef, dictionaryReplacementStatements)
62
- ingredientsParallel.astFunctionDef = Z0Z_inlineThisFunctionWithTheseValues(ingredientsParallel.astFunctionDef, dictionaryReplacementStatements)
63
- ingredientsSequential.astFunctionDef = Z0Z_inlineThisFunctionWithTheseValues(ingredientsSequential.astFunctionDef, dictionaryReplacementStatements)
64
-
65
- # assignRecipeIdentifiersToCallable. =============================
66
- # TODO How can I use `RecipeSynthesizeFlow` as the SSOT for the pairs of items that may need to be replaced?
67
- # NOTE reminder: you are updating these `ast.Name` here (and not in a more general search) because this is a
68
- # narrow search for `ast.Call` so you won't accidentally replace unrelated `ast.Name`.
69
- listFindReplace = [(numbaFlow.sourceCallableDispatcher, numbaFlow.callableDispatcher),
70
- (numbaFlow.sourceCallableInitialize, numbaFlow.callableInitialize),
71
- (numbaFlow.sourceCallableParallel, numbaFlow.callableParallel),
72
- (numbaFlow.sourceCallableSequential, numbaFlow.callableSequential),]
73
- for ingredients in listAllIngredientsFunctions:
74
- for source_Identifier, recipe_Identifier in listFindReplace:
75
- updateCallName = NodeChanger(ifThis.isCall_Identifier(source_Identifier), Then.DOTfunc(Then.replaceWith(Make.Name(recipe_Identifier))))
76
- updateCallName.visit(ingredients.astFunctionDef)
77
-
78
- ingredientsDispatcher.astFunctionDef.name = numbaFlow.callableDispatcher
79
- ingredientsInitialize.astFunctionDef.name = numbaFlow.callableInitialize
80
- ingredientsParallel.astFunctionDef.name = numbaFlow.callableParallel
81
- ingredientsSequential.astFunctionDef.name = numbaFlow.callableSequential
82
-
83
- # Assign identifiers per the recipe. ==============================
84
- listFindReplace = [(numbaFlow.sourceDataclassInstance, numbaFlow.dataclassInstance),
85
- (numbaFlow.sourceDataclassInstanceTaskDistribution, numbaFlow.dataclassInstanceTaskDistribution),
86
- (numbaFlow.sourceConcurrencyManagerNamespace, numbaFlow.concurrencyManagerNamespace),]
87
- for ingredients in listAllIngredientsFunctions:
88
- for source_Identifier, recipe_Identifier in listFindReplace:
89
- updateName = NodeChanger(ifThis.isName_Identifier(source_Identifier), Then.DOTid(Then.replaceWith(recipe_Identifier)))
90
- update_arg = NodeChanger(ifThis.isArgument_Identifier(source_Identifier), Then.DOTarg(Then.replaceWith(recipe_Identifier)))
91
- updateName.visit(ingredients.astFunctionDef)
92
- update_arg.visit(ingredients.astFunctionDef)
93
-
94
- updateConcurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(numbaFlow.sourceConcurrencyManagerNamespace, numbaFlow.sourceConcurrencyManagerIdentifier)
95
- , Then.DOTfunc(Then.replaceWith(Make.Attribute(Make.Name(numbaFlow.concurrencyManagerNamespace), numbaFlow.concurrencyManagerIdentifier))))
96
- updateConcurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
97
-
98
- # shatter Dataclass =======================================================
99
- instance_Identifier = numbaFlow.dataclassInstance
100
- getTheOtherRecord_damn = numbaFlow.dataclassInstanceTaskDistribution
101
- shatteredDataclass = shatter_dataclassesDOTdataclass(numbaFlow.logicalPathModuleDataclass, numbaFlow.sourceDataclassIdentifier, instance_Identifier)
102
- ingredientsDispatcher.imports.update(shatteredDataclass.ledger)
103
-
104
- # Change callable parameters and Call to the callable at the same time ====
105
- # TODO How can I use ast and/or other tools to ensure that when I change a callable, I also change the statements that call the callable?
106
- # Asked differently, how do I integrate separate statements into a "subroutine", and that subroutine is "atomic/indivisible"?
107
- # sequentialCallable =========================================================
108
- ingredientsSequential.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
109
- astCallSequentialCallable = Make.Call(Make.Name(numbaFlow.callableSequential), shatteredDataclass.listName4Parameters)
110
- changeReturnSequentialCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.fragments4AssignmentOrParameters)))
111
- ingredientsSequential.astFunctionDef.returns = shatteredDataclass.signatureReturnAnnotation
112
- replaceAssignSequentialCallable = NodeChanger(ifThis.isAssignAndValueIsCall_Identifier(numbaFlow.callableSequential), Then.replaceWith(Make.Assign(listTargets=[shatteredDataclass.fragments4AssignmentOrParameters], value=astCallSequentialCallable)))
113
-
114
- unpack4sequentialCallable = NodeChanger(ifThis.isAssignAndValueIsCall_Identifier(numbaFlow.callableSequential), Then.insertThisAbove(shatteredDataclass.listUnpack))
115
- repack4sequentialCallable = NodeChanger(ifThis.isAssignAndValueIsCall_Identifier(numbaFlow.callableSequential), Then.insertThisBelow([shatteredDataclass.repack]))
116
-
117
- changeReturnSequentialCallable.visit(ingredientsSequential.astFunctionDef)
118
- replaceAssignSequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
119
- unpack4sequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
120
- repack4sequentialCallable.visit(ingredientsDispatcher.astFunctionDef)
121
-
122
- ingredientsSequential.astFunctionDef = Z0Z_lameFindReplace(ingredientsSequential.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
123
-
124
- # parallelCallable =========================================================
125
- ingredientsParallel.astFunctionDef.args = Make.argumentsSpecification(args=shatteredDataclass.list_argAnnotated4ArgumentsSpecification)
126
- replaceCall2concurrencyManager = NodeChanger(ifThis.isCallAttributeNamespace_Identifier(numbaFlow.concurrencyManagerNamespace, numbaFlow.concurrencyManagerIdentifier), Then.replaceWith(Make.Call(Make.Attribute(Make.Name(numbaFlow.concurrencyManagerNamespace), numbaFlow.concurrencyManagerIdentifier), listArguments=[Make.Name(numbaFlow.callableParallel)] + shatteredDataclass.listName4Parameters)))
127
-
128
- # NOTE I am dissatisfied with this logic for many reasons, including that it requires separate NodeCollector and NodeReplacer instances.
129
- astCallConcurrencyResult: list[ast.Call] = []
130
- get_astCallConcurrencyResult: NodeTourist = NodeTourist(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), lambda node: NodeTourist(be.Call, Then.appendTo(astCallConcurrencyResult)).visit(node)) # pyright: ignore[reportUnknownArgumentType, reportUnknownLambdaType]
131
- get_astCallConcurrencyResult.visit(ingredientsDispatcher.astFunctionDef)
132
- replaceAssignParallelCallable = NodeChanger(ifThis.isAssignAndTargets0Is(ifThis.isSubscript_Identifier(getTheOtherRecord_damn)), Then.DOTvalue(Then.replaceWith(astCallConcurrencyResult[0])))
133
- replaceAssignParallelCallable.visit(ingredientsDispatcher.astFunctionDef)
134
- changeReturnParallelCallable = NodeChanger(be.Return, Then.replaceWith(Make.Return(shatteredDataclass.countingVariableName)))
135
- ingredientsParallel.astFunctionDef.returns = shatteredDataclass.countingVariableAnnotation
136
-
137
- unpack4parallelCallable = NodeChanger(ifThis.isAssignAndValueIsCallAttributeNamespace_Identifier(numbaFlow.concurrencyManagerNamespace, numbaFlow.concurrencyManagerIdentifier), Then.insertThisAbove(shatteredDataclass.listUnpack))
138
-
139
- unpack4parallelCallable.visit(ingredientsDispatcher.astFunctionDef)
140
- replaceCall2concurrencyManager.visit(ingredientsDispatcher.astFunctionDef)
141
- changeReturnParallelCallable.visit(ingredientsParallel.astFunctionDef)
142
-
143
- ingredientsParallel.astFunctionDef = Z0Z_lameFindReplace(ingredientsParallel.astFunctionDef, shatteredDataclass.map_stateDOTfield2Name)
144
-
145
- # numba decorators =========================================
146
- ingredientsParallel = decorateCallableWithNumba(ingredientsParallel)
147
- ingredientsSequential = decorateCallableWithNumba(ingredientsSequential)
148
-
149
- # Module-level transformations ===========================================================
150
- ingredientsModuleNumbaUnified = IngredientsModule(ingredientsFunction=listAllIngredientsFunctions, imports=LedgerOfImports(numbaFlow.source_astModule))
151
-
152
- write_astModule(ingredientsModuleNumbaUnified, numbaFlow.pathFilenameDispatcher, numbaFlow.packageIdentifier)
153
-
154
- theNumbaFlow: RecipeSynthesizeFlow = RecipeSynthesizeFlow()
155
- if __name__ == '__main__':
156
- makeNumbaFlow(theNumbaFlow)
@@ -1,235 +0,0 @@
1
- """
2
- Utilities for transforming complex data structures in Python code generation.
3
-
4
- This module provides specialized tools for working with structured data types during
5
- the code transformation process, with a particular focus on handling dataclasses. It
6
- implements functionality that enables:
7
-
8
- 1. Decomposing dataclasses into individual fields for efficient processing
9
- 2. Creating optimized parameter passing for transformed functions
10
- 3. Converting between different representations of data structures
11
- 4. Serializing and deserializing computation state objects
12
-
13
- The core functionality revolves around the "shattering" process that breaks down
14
- a dataclass into its constituent components, making each field individually accessible
15
- for code generation and optimization purposes. This dataclass handling is critical for
16
- transforming algorithms that operate on unified state objects into optimized implementations
17
- that work with primitive types directly.
18
-
19
- While developed for transforming map folding computation state objects, the utilities are
20
- designed to be applicable to various data structure transformation scenarios.
21
- """
22
-
23
- from collections.abc import Callable
24
- from copy import deepcopy
25
- from mapFolding.beDRY import outfitCountFolds
26
- from mapFolding.filesystem import getPathFilenameFoldsTotal
27
- from mapFolding.someAssemblyRequired import (
28
- ast_Identifier,
29
- be,
30
- extractClassDef,
31
- ifThis,
32
- ImaAnnotationType,
33
- importLogicalPath2Callable,
34
- Make,
35
- NodeTourist,
36
- parseLogicalPath2astModule,
37
- str_nameDOTname,
38
- Then,
39
- 又,
40
- )
41
- from mapFolding.someAssemblyRequired._toolboxContainers import LedgerOfImports
42
- from mapFolding.theSSOT import ComputationState, raiseIfNoneGitHubIssueNumber3, The
43
- from os import PathLike
44
- from pathlib import Path, PurePath
45
- from typing import Any, Literal, overload
46
- import ast
47
- import dataclasses
48
- import pickle
49
-
50
- # Create dummy AST elements for use as defaults
51
- dummyAssign = Make.Assign([Make.Name("dummyTarget")], Make.Constant(None))
52
- dummySubscript = Make.Subscript(Make.Name("dummy"), Make.Name("slice"))
53
- dummyTuple = Make.Tuple([Make.Name("dummyElement")])
54
-
55
- @dataclasses.dataclass
56
- class ShatteredDataclass:
57
- countingVariableAnnotation: ImaAnnotationType
58
- """Type annotation for the counting variable extracted from the dataclass."""
59
- countingVariableName: ast.Name
60
- """AST name node representing the counting variable identifier."""
61
- field2AnnAssign: dict[ast_Identifier, ast.AnnAssign] = dataclasses.field(default_factory=dict)
62
- """Maps field names to their corresponding AST call expressions."""
63
- Z0Z_field2AnnAssign: dict[ast_Identifier, tuple[ast.AnnAssign, str]] = dataclasses.field(default_factory=dict)
64
- fragments4AssignmentOrParameters: ast.Tuple = dummyTuple
65
- """AST tuple used as target for assignment to capture returned fragments."""
66
- ledger: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
67
- """Import records for the dataclass and its constituent parts."""
68
- list_argAnnotated4ArgumentsSpecification: list[ast.arg] = dataclasses.field(default_factory=list)
69
- """Function argument nodes with annotations for parameter specification."""
70
- list_keyword_field__field4init: list[ast.keyword] = dataclasses.field(default_factory=list)
71
- """Keyword arguments for dataclass initialization with field=field format."""
72
- listAnnotations: list[ImaAnnotationType] = dataclasses.field(default_factory=list)
73
- """Type annotations for each dataclass field."""
74
- listName4Parameters: list[ast.Name] = dataclasses.field(default_factory=list)
75
- """Name nodes for each dataclass field used as function parameters."""
76
- listUnpack: list[ast.AnnAssign] = dataclasses.field(default_factory=list)
77
- """Annotated assignment statements to extract fields from dataclass."""
78
- map_stateDOTfield2Name: dict[ast.expr, ast.Name] = dataclasses.field(default_factory=dict)
79
- """Maps AST expressions to Name nodes for find-replace operations."""
80
- repack: ast.Assign = dummyAssign
81
- """AST assignment statement that reconstructs the original dataclass instance."""
82
- signatureReturnAnnotation: ast.Subscript = dummySubscript
83
- """tuple-based return type annotation for function definitions."""
84
-
85
- @dataclasses.dataclass
86
- class DeReConstructField2ast:
87
- dataclassesDOTdataclassLogicalPathModule: dataclasses.InitVar[str_nameDOTname]
88
- dataclassClassDef: dataclasses.InitVar[ast.ClassDef]
89
- dataclassesDOTdataclassInstance_Identifier: dataclasses.InitVar[ast_Identifier]
90
- field: dataclasses.InitVar[dataclasses.Field[Any]]
91
-
92
- ledger: LedgerOfImports = dataclasses.field(default_factory=LedgerOfImports)
93
-
94
- name: ast_Identifier = dataclasses.field(init=False)
95
- typeBuffalo: type[Any] | str | Any = dataclasses.field(init=False)
96
- default: Any | None = dataclasses.field(init=False)
97
- default_factory: Callable[..., Any] | None = dataclasses.field(init=False)
98
- repr: bool = dataclasses.field(init=False)
99
- hash: bool | None = dataclasses.field(init=False)
100
- init: bool = dataclasses.field(init=False)
101
- compare: bool = dataclasses.field(init=False)
102
- metadata: dict[Any, Any] = dataclasses.field(init=False)
103
- kw_only: bool = dataclasses.field(init=False)
104
-
105
- astName: ast.Name = dataclasses.field(init=False)
106
- ast_keyword_field__field: ast.keyword = dataclasses.field(init=False)
107
- ast_nameDOTname: ast.Attribute = dataclasses.field(init=False)
108
- astAnnotation: ImaAnnotationType = dataclasses.field(init=False)
109
- ast_argAnnotated: ast.arg = dataclasses.field(init=False)
110
- astAnnAssignConstructor: ast.AnnAssign = dataclasses.field(init=False)
111
- Z0Z_hack: tuple[ast.AnnAssign, str] = dataclasses.field(init=False)
112
-
113
- def __post_init__(self, dataclassesDOTdataclassLogicalPathModule: str_nameDOTname, dataclassClassDef: ast.ClassDef, dataclassesDOTdataclassInstance_Identifier: ast_Identifier, field: dataclasses.Field[Any]) -> None:
114
- self.compare = field.compare
115
- self.default = field.default if field.default is not dataclasses.MISSING else None
116
- self.default_factory = field.default_factory if field.default_factory is not dataclasses.MISSING else None
117
- self.hash = field.hash
118
- self.init = field.init
119
- self.kw_only = field.kw_only if field.kw_only is not dataclasses.MISSING else False
120
- self.metadata = dict(field.metadata)
121
- self.name = field.name
122
- self.repr = field.repr
123
- self.typeBuffalo = field.type
124
-
125
- self.astName = Make.Name(self.name)
126
- self.ast_keyword_field__field = Make.keyword(self.name, self.astName)
127
- self.ast_nameDOTname = Make.Attribute(Make.Name(dataclassesDOTdataclassInstance_Identifier), self.name)
128
-
129
- sherpa = NodeTourist(ifThis.isAnnAssign_targetIs(ifThis.isName_Identifier(self.name)), 又.annotation(Then.getIt)).captureLastMatch(dataclassClassDef)
130
- if sherpa is None: raise raiseIfNoneGitHubIssueNumber3
131
- else: self.astAnnotation = sherpa
132
-
133
- self.ast_argAnnotated = Make.arg(self.name, self.astAnnotation)
134
-
135
- dtype = self.metadata.get('dtype', None)
136
- if dtype:
137
- constructor = 'array'
138
- self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(Make.Name(constructor), list_astKeywords=[Make.keyword('dtype', Make.Name(dtype.__name__))]))
139
- self.ledger.addImportFrom_asStr('numpy', constructor)
140
- self.ledger.addImportFrom_asStr('numpy', dtype.__name__)
141
- self.Z0Z_hack = (self.astAnnAssignConstructor, 'array')
142
- elif be.Name(self.astAnnotation):
143
- self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, Make.Call(self.astAnnotation, [Make.Constant(-1)]))
144
- self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id)
145
- self.Z0Z_hack = (self.astAnnAssignConstructor, 'scalar')
146
- elif be.Subscript(self.astAnnotation):
147
- elementConstructor: ast_Identifier = self.metadata['elementConstructor']
148
- self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, elementConstructor)
149
- takeTheTuple: ast.Tuple = deepcopy(self.astAnnotation.slice)
150
- self.astAnnAssignConstructor = Make.AnnAssign(self.astName, self.astAnnotation, takeTheTuple)
151
- self.Z0Z_hack = (self.astAnnAssignConstructor, elementConstructor)
152
- if be.Name(self.astAnnotation):
153
- self.ledger.addImportFrom_asStr(dataclassesDOTdataclassLogicalPathModule, self.astAnnotation.id) # pyright: ignore [reportUnknownArgumentType, reportUnknownMemberType, reportIJustCalledATypeGuardMethod_WTF]
154
-
155
- def shatter_dataclassesDOTdataclass(logicalPathModule: str_nameDOTname, dataclass_Identifier: ast_Identifier, instance_Identifier: ast_Identifier) -> ShatteredDataclass:
156
- """
157
- Parameters:
158
- logicalPathModule: gimme string cuz python is stoopid
159
- dataclass_Identifier: The identifier of the dataclass to be dismantled.
160
- instance_Identifier: In the synthesized module/function/scope, the identifier that will be used for the instance.
161
- """
162
- Official_fieldOrder: list[ast_Identifier] = []
163
- dictionaryDeReConstruction: dict[ast_Identifier, DeReConstructField2ast] = {}
164
-
165
- dataclassClassDef = extractClassDef(parseLogicalPath2astModule(logicalPathModule), dataclass_Identifier)
166
- if not isinstance(dataclassClassDef, ast.ClassDef): raise ValueError(f"I could not find {dataclass_Identifier=} in {logicalPathModule=}.")
167
-
168
- countingVariable = None
169
- for aField in dataclasses.fields(importLogicalPath2Callable(logicalPathModule, dataclass_Identifier)): # pyright: ignore [reportArgumentType]
170
- Official_fieldOrder.append(aField.name)
171
- dictionaryDeReConstruction[aField.name] = DeReConstructField2ast(logicalPathModule, dataclassClassDef, instance_Identifier, aField)
172
- if aField.metadata.get('theCountingIdentifier', False):
173
- countingVariable = dictionaryDeReConstruction[aField.name].name
174
-
175
- if countingVariable is None:
176
- raise ValueError(f"I could not find the counting variable in {dataclass_Identifier=} in {logicalPathModule=}.")
177
-
178
- shatteredDataclass = ShatteredDataclass(
179
- countingVariableAnnotation=dictionaryDeReConstruction[countingVariable].astAnnotation,
180
- countingVariableName=dictionaryDeReConstruction[countingVariable].astName,
181
- field2AnnAssign={dictionaryDeReConstruction[field].name: dictionaryDeReConstruction[field].astAnnAssignConstructor for field in Official_fieldOrder},
182
- Z0Z_field2AnnAssign={dictionaryDeReConstruction[field].name: dictionaryDeReConstruction[field].Z0Z_hack for field in Official_fieldOrder},
183
- list_argAnnotated4ArgumentsSpecification=[dictionaryDeReConstruction[field].ast_argAnnotated for field in Official_fieldOrder],
184
- list_keyword_field__field4init=[dictionaryDeReConstruction[field].ast_keyword_field__field for field in Official_fieldOrder if dictionaryDeReConstruction[field].init],
185
- listAnnotations=[dictionaryDeReConstruction[field].astAnnotation for field in Official_fieldOrder],
186
- listName4Parameters=[dictionaryDeReConstruction[field].astName for field in Official_fieldOrder],
187
- listUnpack=[Make.AnnAssign(dictionaryDeReConstruction[field].astName, dictionaryDeReConstruction[field].astAnnotation, dictionaryDeReConstruction[field].ast_nameDOTname) for field in Official_fieldOrder],
188
- map_stateDOTfield2Name={dictionaryDeReConstruction[field].ast_nameDOTname: dictionaryDeReConstruction[field].astName for field in Official_fieldOrder},
189
- )
190
- shatteredDataclass.fragments4AssignmentOrParameters = Make.Tuple(shatteredDataclass.listName4Parameters, ast.Store())
191
- shatteredDataclass.repack = Make.Assign(listTargets=[Make.Name(instance_Identifier)], value=Make.Call(Make.Name(dataclass_Identifier), list_astKeywords=shatteredDataclass.list_keyword_field__field4init))
192
- shatteredDataclass.signatureReturnAnnotation = Make.Subscript(Make.Name('tuple'), Make.Tuple(shatteredDataclass.listAnnotations))
193
-
194
- shatteredDataclass.ledger.update(*(dictionaryDeReConstruction[field].ledger for field in Official_fieldOrder))
195
- shatteredDataclass.ledger.addImportFrom_asStr(logicalPathModule, dataclass_Identifier)
196
-
197
- return shatteredDataclass
198
-
199
- @overload
200
- def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: Literal[True], *, pathFilename: PathLike[str] | PurePath | None = None, **keywordArguments: Any) -> Path: ...
201
- @overload
202
- def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: Literal[False] = False, **keywordArguments: Any) -> ComputationState: ...
203
- def makeInitializedComputationState(mapShape: tuple[int, ...], writeJob: bool = False, *, pathFilename: PathLike[str] | PurePath | None = None, **keywordArguments: Any) -> ComputationState | Path:
204
- """
205
- Initializes a computation state and optionally saves it to disk.
206
-
207
- This function initializes a computation state using the source algorithm.
208
-
209
- Hint: If you want an uninitialized state, call `outfitCountFolds` directly.
210
-
211
- Parameters:
212
- mapShape: List of integers representing the dimensions of the map to be folded.
213
- writeJob (False): Whether to save the state to disk.
214
- pathFilename (getPathFilenameFoldsTotal.pkl): The path and filename to save the state. If None, uses a default path.
215
- **keywordArguments: computationDivisions:int|str|None=None,concurrencyLimit:int=1.
216
- Returns:
217
- stateUniversal|pathFilenameJob: The computation state for the map folding calculations, or
218
- the path to the saved state file if writeJob is True.
219
- """
220
- stateUniversal: ComputationState = outfitCountFolds(mapShape, **keywordArguments)
221
-
222
- initializeState = importLogicalPath2Callable(The.logicalPathModuleSourceAlgorithm, The.sourceCallableInitialize)
223
- stateUniversal = initializeState(stateUniversal)
224
-
225
- if not writeJob:
226
- return stateUniversal
227
-
228
- if pathFilename:
229
- pathFilenameJob = Path(pathFilename)
230
- pathFilenameJob.parent.mkdir(parents=True, exist_ok=True)
231
- else:
232
- pathFilenameJob = getPathFilenameFoldsTotal(stateUniversal.mapShape).with_suffix('.pkl')
233
-
234
- pathFilenameJob.write_bytes(pickle.dumps(stateUniversal))
235
- return pathFilenameJob
@@ -1,49 +0,0 @@
1
- mapFolding/__init__.py,sha256=z3joPk4hgIbSEsIWGgkOukl-nrgI5u4wg0mRJ7aSRHM,1982
2
- mapFolding/basecamp.py,sha256=CGJOUE0eQsS4EzHjptzJbxg-Oy4t6TsE7P3vgTRuAww,4763
3
- mapFolding/beDRY.py,sha256=UhH52BryHQNRjphf_PirtMkV45rhdemdC9PmnpACq7I,9397
4
- mapFolding/filesystem.py,sha256=JyeFLlkeMqhsYGp80ViwDjrrDgFaTTdGTewLtla-m00,7132
5
- mapFolding/noHomeYet.py,sha256=UKZeWlyn0SKlF9dhYoud7E6gWXpiSEekZOOoJp88WeI,1362
6
- mapFolding/oeis.py,sha256=F1tGo2kT9xR3eRaXyxH0bnAWscbn38VS2fIY8-VlLZs,12616
7
- mapFolding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- mapFolding/theDao.py,sha256=MVopt1LzhdIQYA97SEoq9bdzct6hbK0lEyPxBAAlVTc,9934
9
- mapFolding/theSSOT.py,sha256=JCejbNNVP6s9dR2vkdttKXleyZrQroYRWUjGQrXyFaI,11845
10
- mapFolding/reference/__init__.py,sha256=UIEU8BJR_YDzjFQcLel3XtHzOCJiOUGlGiWzOzbvhik,2206
11
- mapFolding/reference/flattened.py,sha256=QK1xG9SllqCoi68e86Hyl9d9ATUAAFNpTQI-3zmcp5I,16072
12
- mapFolding/reference/hunterNumba.py,sha256=espFiX92EPZ1Ub1YQVoBnNYvh2kFg1HR6Qa4djx8Ixg,7253
13
- mapFolding/reference/irvineJavaPort.py,sha256=UEfIX4QbPLl5jnyfYIyX5YRR3_rYvPUikK8jLehsFko,4076
14
- mapFolding/reference/jaxCount.py,sha256=TuDNKOnyhQfuixKmIxO9Algv7dvy7KMGhgsV3h96FGE,14853
15
- mapFolding/reference/lunnanNumpy.py,sha256=mMgrgbrBpe4nmo72ThEI-MGH0OwEHmfMPczSXHp2qKo,4357
16
- mapFolding/reference/lunnanWhile.py,sha256=ZL8GAQtPs5nJZSgoDl5USrLSS_zs03y98y1Z9E4jOmQ,3799
17
- mapFolding/reference/rotatedEntryPoint.py,sha256=5ughpKUT2JQhoAKgoDUdYNjgWQYPGV8v-7dWEAdDmfE,10274
18
- mapFolding/reference/total_countPlus1vsPlusN.py,sha256=yJZAVLVdoXqHag2_N6_6CT-Q6HXBgRro-eny93-Rlpw,9307
19
- mapFolding/reference/jobsCompleted/__init__.py,sha256=TU93ZGUW1xEkT6d9mQFn_rp5DvRy0ZslEB2Q6MF5ZDc,2596
20
- mapFolding/reference/jobsCompleted/[2x19]/p2x19.py,sha256=_tvYtfzMWVo2VtUbIAieoscb4N8FFflgTdW4-ljBUuA,19626
21
- mapFolding/reference/jobsCompleted/p2x19/p2x19.py,sha256=eZEw4Me4ocTt6VXoK2-Sbd5SowZtxRIbN9dZmc7OCVg,6395
22
- mapFolding/someAssemblyRequired/__init__.py,sha256=v8aBRcRa4d_34SUNu_mZC5K_NC7-buy849k2nnk9lTE,2704
23
- mapFolding/someAssemblyRequired/_theTypes.py,sha256=nHFgpts-hVy2lNbyvIyP5JeG-ikaIlbRaMUPnEDwzhc,1924
24
- mapFolding/someAssemblyRequired/_tool_Make.py,sha256=nIAjiq-Q2sBvO_bVEGFh3Z79vXmcqcfJCtuhR4Vyzqw,7081
25
- mapFolding/someAssemblyRequired/_tool_Then.py,sha256=8ZCI8A6-EtUo02m71h60iJwnWfdwxWiQT0_OWJDFBho,2566
26
- mapFolding/someAssemblyRequired/_toolboxAntecedents.py,sha256=USqFn2HKiu2aUYUrUI-UVRq2tAvVw2hLtH2uLJ61ZbY,19110
27
- mapFolding/someAssemblyRequired/_toolboxContainers.py,sha256=jjGyKhblHqDiCzjHRfyqnI2UHNzJTe3aBwNofukcf88,15998
28
- mapFolding/someAssemblyRequired/_toolboxPython.py,sha256=OSFMNBiyBofzckF6J8OYibm9Zh3GHXB8bJrMggVT8fw,3801
29
- mapFolding/someAssemblyRequired/getLLVMforNoReason.py,sha256=CDbesDJSQE-P8uznXIAttRw9f413UpUt-RowK38hqbY,2735
30
- mapFolding/someAssemblyRequired/ingredientsNumba.py,sha256=Qb2WVDv5XszwcQCs3zFrodS7G0GKstSqVrKDXBzdMaw,8128
31
- mapFolding/someAssemblyRequired/synthesizeNumbaFlow.py,sha256=A9H6gbTNtK0bihxJ98Xv5ePzGeOyYP6e0Fxmj2NTh-M,11186
32
- mapFolding/someAssemblyRequired/synthesizeNumbaJob.py,sha256=_TPANqYkWS4O2IhU4EOatRv-OetfFCfk5b6rrxB5i48,13210
33
- mapFolding/someAssemblyRequired/transformDataStructures.py,sha256=I4OtFUF7ZNpEzsAbIbcqTifnY1vIwN0v1f-XFQQp3Wk,13558
34
- mapFolding/someAssemblyRequired/transformationTools.py,sha256=fXr3XKHuFntKLTxoau9ZnbZJRPOG3aS2BhjJvkgRXRs,7932
35
- mapFolding/syntheticModules/__init__.py,sha256=evVFqhCGa-WZKDiLcnQWjs-Bj34eRnfSLqz_d7dFYZY,83
36
- mapFolding/syntheticModules/numbaCount_doTheNeedful.py,sha256=3thXThbv2Xo0t_cRGzMbHPFXTBmLClmKejR_Ibu_jOo,15697
37
- mapfolding-0.8.4.dist-info/licenses/LICENSE,sha256=NxH5Y8BdC-gNU-WSMwim3uMbID2iNDXJz7fHtuTdXhk,19346
38
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- tests/conftest.py,sha256=-5rAnacJAuDcG84jeoQAA35GvUTW6YTcT6E_xU7EXQ0,11587
40
- tests/test_computations.py,sha256=R5gea8liIE_rBYvgRDIby6GljBazuGgqCeYcqKRjORg,3449
41
- tests/test_filesystem.py,sha256=RplMT0GULGQxomQSbk5wLlvNsj7ehDlZmzayatJopp4,3150
42
- tests/test_oeis.py,sha256=uxvwmgbnylSDdsVJfuAT0LuYLbIVFwSgdLxHm-xUGBM,5043
43
- tests/test_other.py,sha256=AzsCXiX8x5WJ7i0SocWQY6lT30IJg1lKoybx03X2eqU,4281
44
- tests/test_tasks.py,sha256=hkZygihT8bCEO2zc-2VcxReQrZJBwgLNbYx0YP4lTDg,2853
45
- mapfolding-0.8.4.dist-info/METADATA,sha256=NMXRV8HTCO8Nab_q1Bo3W6i0eAirJ9K453LRGOv86i8,9326
46
- mapfolding-0.8.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
47
- mapfolding-0.8.4.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
48
- mapfolding-0.8.4.dist-info/top_level.txt,sha256=1gP2vFaqPwHujGwb3UjtMlLEGN-943VSYFR7V4gDqW8,17
49
- mapfolding-0.8.4.dist-info/RECORD,,