mapFolding 0.3.7__py3-none-any.whl → 0.3.8__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.
citations/constants.py ADDED
@@ -0,0 +1,3 @@
1
+ GITHUB_API_VERSION_HEADER = {
2
+ "X-GitHub-Api-Version": "2022-11-28"
3
+ }
@@ -1,14 +1,16 @@
1
1
  from cffconvert.cli.create_citation import create_citation
2
+ from mapFolding.citations.constants import GITHUB_API_VERSION_HEADER
2
3
  from packaging.metadata import Metadata as PyPAMetadata
3
4
  from typing import Any, Dict, List
4
5
  import attrs
5
6
  import cffconvert
6
- import tempfile
7
+ import os
7
8
  import packaging
8
9
  import packaging.metadata
9
10
  import packaging.utils
10
11
  import packaging.version
11
12
  import pathlib
13
+ import requests
12
14
  import ruamel.yaml
13
15
  import tomli
14
16
 
@@ -23,7 +25,7 @@ Tentative plan:
23
25
  - pathFilenameCitationDOTcffRepo
24
26
  - Commit and push to GitHub
25
27
  - this complicates things
26
- - I want the updated citation to be in the `commit` field of itself
28
+ - I want the updated citation to be in the `commit` field of itself: but the commit field isn't even working right now
27
29
  """
28
30
 
29
31
  @attrs.define
@@ -31,28 +33,30 @@ class CitationNexus:
31
33
  """
32
34
  - one-to-one correlation with `cffconvert.lib.cff_1_2_x.citation` class Citation_1_2_x.cffobj
33
35
  """
34
- cffDASHversion: str # pathFilenameCitationSSOT
35
- message: str # pathFilenameCitationSSOT
36
-
37
- abstract: str | None = None # pathFilenameCitationSSOT
38
- authors: list[dict[str,str]] = attrs.field(factory=list) # pathFilenamePackageSSOT; pyproject.toml authors
39
- commit: str | None = None # workflows['Make GitHub Release']
40
- contact: list[dict[str,str]] = attrs.field(factory=list) # pathFilenamePackageSSOT; pyproject.toml maintainers
41
- dateDASHreleased: str | None = None # workflows['Make GitHub Release']
42
- doi: str | None = None # pathFilenameCitationSSOT
43
- identifiers: list[str] = attrs.field(factory=list) # workflows['Make GitHub Release']
44
- keywords: list[str] = attrs.field(factory=list) # pathFilenamePackageSSOT; packaging.metadata.Metadata.keywords
45
- license: str | None = None # pathFilenamePackageSSOT; packaging.metadata.Metadata.license_expression
46
- licenseDASHurl: str | None = None # pathFilenamePackageSSOT; packaging.metadata.Metadata.project_urls: license or pyproject.toml urls license
47
- preferredDASHcitation: str | None = None # pathFilenameCitationSSOT
48
- references: list[str] = attrs.field(factory=list) # bibtex files in pathCitationSSOT. Conversion method and timing TBD.
49
- repositoryDASHartifact: str | None = None # (https://pypi.org/pypi/{package_name}/json').json()['releases']
50
- repositoryDASHcode: str | None = None # workflows['Make GitHub Release']
51
- repository: str | None = None # pathFilenamePackageSSOT; packaging.metadata.Metadata.project_urls: repository
52
- title: str | None = None # pathFilenamePackageSSOT; pyproject.toml name (packaging normalizes the names)
53
- type: str | None = None # pathFilenameCitationSSOT
54
- url: str | None = None # pathFilenamePackageSSOT; packaging.metadata.Metadata.project_urls: homepage
55
- version: str | None = None # pathFilenamePackageSSOT; packaging.metadata.Metadata.version
36
+ cffDASHversion: str
37
+ message: str
38
+
39
+ abstract: str | None = None
40
+ authors: list[dict[str,str]] = attrs.field(factory=list)
41
+ # GitHub TODO
42
+ commit: str | None = None
43
+ contact: list[dict[str,str]] = attrs.field(factory=list)
44
+ dateDASHreleased: str | None = None
45
+ doi: str | None = None
46
+ identifiers: list[str] = attrs.field(factory=list)
47
+ keywords: list[str] = attrs.field(factory=list)
48
+ license: str | None = None
49
+ licenseDASHurl: str | None = None
50
+ preferredDASHcitation: str | None = None
51
+ # TODO bibtex files in pathCitationSSOT. Conversion method and timing TBD.
52
+ references: list[str] = attrs.field(factory=list)
53
+ repository: str | None = None
54
+ repositoryDASHartifact: str | None = None
55
+ repositoryDASHcode: str | None = None
56
+ title: str | None = None
57
+ type: str | None = None
58
+ url: str | None = None
59
+ version: str | None = None
56
60
 
57
61
  def setInStone(self, prophet: str) -> "CitationNexus":
58
62
  match prophet:
@@ -61,12 +65,19 @@ class CitationNexus:
61
65
  # "freeze" these items
62
66
  # setattr(self.cffDASHversion, 'type', Final[str])
63
67
  # setattr(self.doi, 'type', Final[str])
64
- # cffDASHversion: str # pathFilenameCitationSSOT
65
- # message: str # pathFilenameCitationSSOT
66
- # abstract: str | None = None # pathFilenameCitationSSOT
67
- # doi: str | None = None # pathFilenameCitationSSOT
68
- # preferredDASHcitation: str | None = None # pathFilenameCitationSSOT
69
- # type: str | None = None # pathFilenameCitationSSOT
68
+ # cffDASHversion: str
69
+ # message: str
70
+ # abstract: str | None = None
71
+ # doi: str | None = None
72
+ # preferredDASHcitation: str | None = None
73
+ # type: str | None = None
74
+ case "GitHub":
75
+ pass
76
+ # "freeze" these items
77
+ # setattr(self.commit, 'type', Final[str])
78
+ # setattr(self.dateDASHreleased, 'type', Final[str])
79
+ # setattr(self.identifiers, 'type', Final[list[str]])
80
+ # setattr(self.repositoryDASHcode, 'type', Final[str])
70
81
  case "PyPA":
71
82
  pass
72
83
  # "freeze" these items
@@ -76,6 +87,10 @@ class CitationNexus:
76
87
  # setattr(self.repository, 'type', Final[str])
77
88
  # setattr(self.url, 'type', Final[str])
78
89
  # setattr(self.version, 'type', Final[str])
90
+ case "PyPI":
91
+ pass
92
+ # "freeze" these items
93
+ # setattr(self.repositoryDASHartifact, 'type', Final[str])
79
94
  case "pyprojectDOTtoml":
80
95
  pass
81
96
  # "freeze" these items
@@ -84,53 +99,6 @@ class CitationNexus:
84
99
  # setattr(self.title, 'type', Final[str])
85
100
  return self
86
101
 
87
- def getNexusCitation(pathFilenameCitationSSOT: pathlib.Path) -> CitationNexus:
88
-
89
- # `cffconvert.cli.create_citation.create_citation()` is PAINFULLY mundane, but a major problem
90
- # in the CFF ecosystem is divergence. Therefore, I will use this function so that my code
91
- # converges with the CFF ecosystem.
92
- citationObject: cffconvert.Citation = create_citation(infile=pathFilenameCitationSSOT, url=None)
93
- # `._parse()` is a yaml loader: use it for convergence
94
- cffobj: Dict[Any, Any] = citationObject._parse()
95
-
96
- nexusCitation = CitationNexus(
97
- cffDASHversion=cffobj["cff-version"],
98
- message=cffobj["message"],
99
- )
100
-
101
- Z0Z_list: List[attrs.Attribute] = list(attrs.fields(type(nexusCitation)))
102
- for Z0Z_field in Z0Z_list:
103
- cffobjKeyName: str = Z0Z_field.name.replace("DASH", "-")
104
- cffobjValue = cffobj.get(cffobjKeyName)
105
- if cffobjValue: # An empty list will be False
106
- setattr(nexusCitation, Z0Z_field.name, cffobjValue)
107
-
108
- nexusCitation = nexusCitation.setInStone("Citation")
109
- return nexusCitation
110
-
111
- def getPypaMetadata(packageData: Dict[str, Any]) -> PyPAMetadata:
112
- """
113
- Create a PyPA metadata object (version 2.4) from packageData.
114
- https://packaging.python.org/en/latest/specifications/core-metadata/
115
- """
116
- dictionaryProjectURLs: Dict[str, str] = {}
117
- for urlName, url in packageData.get("urls", {}).items():
118
- urlName = urlName.lower()
119
- if urlName in listProjectURLsTarget:
120
- dictionaryProjectURLs[urlName] = url
121
-
122
- metadataRaw = packaging.metadata.RawMetadata(
123
- keywords=packageData.get("keywords", []),
124
- license_expression=packageData.get("license", {}).get("text", ""),
125
- metadata_version="2.4",
126
- name=packaging.utils.canonicalize_name(packageData.get("name", None), validate=True), # packaging.metadata.InvalidMetadata: 'name' is a required field
127
- project_urls=dictionaryProjectURLs,
128
- version=packageData.get("version", None),
129
- )
130
-
131
- metadata = PyPAMetadata().from_raw(metadataRaw)
132
- return metadata
133
-
134
102
  def addPypaMetadata(nexusCitation: CitationNexus, metadata: PyPAMetadata) -> CitationNexus:
135
103
  if not metadata.name:
136
104
  raise ValueError("Metadata name is required.")
@@ -179,6 +147,146 @@ def add_pyprojectDOTtoml(nexusCitation: CitationNexus, packageData: Dict[str, An
179
147
  nexusCitation = nexusCitation.setInStone("pyprojectDOTtoml")
180
148
  return nexusCitation
181
149
 
150
+ def getGitHubRelease(nexusCitation: CitationNexus) -> Dict[str, Any]:
151
+ """Return a dictionary with GitHub release data.
152
+
153
+ The dictionary contains the following keys:
154
+ commit: The commit hash (using the API field 'target_commitish').
155
+ date-released: The published date (in YYYY-MM-DD format).
156
+ identifiers: A list with one identifier object, whose description is
157
+ 'The URL for {nexusCitation.title} {nexusCitation.version}.'
158
+ repository-code: A URL for the commit in the repository.
159
+
160
+ Raises:
161
+ ValueError: If the nexusCitation.repository is not set or cannot be parsed.
162
+ RuntimeError: If the HTTP request to GitHub fails.
163
+ """
164
+ if not nexusCitation.repository:
165
+ raise ValueError("Repository URL is required to get GitHub release info.")
166
+
167
+ urlparts = nexusCitation.repository.replace("https://github.com", "", 1).strip("/").split("/") + [None] * 5
168
+ ownername, reponame, _2, refvalue, *_filename_parts = urlparts
169
+ reponame = reponame.replace(".git", "") # type: ignore # Remove .git from the repository name, if present.
170
+ assert ownername is not None, "URL should include the name of the owner/organization."
171
+ assert reponame is not None, "URL should include the name of the repository."
172
+ if refvalue is None:
173
+ repos_api = f"https://api.github.com/repos/{ownername}/{reponame}/releases/latest"
174
+ headers = GITHUB_API_VERSION_HEADER
175
+ headers.update({"Accept": "application/vnd.github+json"})
176
+ token = os.environ.get("GITHUB_TOKEN")
177
+ headers.update({"Authorization": f"Bearer { token }"})
178
+ response = requests.get(repos_api, headers=headers)
179
+ if response.status_code != 200:
180
+ raise RuntimeError(f"Failed to get GitHub release info: {response.status_code}")
181
+
182
+ releaseData = response.json()
183
+ # commitHash = releaseData.get("target_commitish")
184
+ publishedAt = releaseData.get("published_at")
185
+ if publishedAt:
186
+ # Convert ISO timestamp (e.g., "2020-12-31T12:34:56Z") to "YYYY-MM-DD".
187
+ publishedAt = publishedAt.split("T")[0]
188
+
189
+ releaseHtmlUrl = releaseData.get("html_url")
190
+ identifierDescription = f"The URL for {nexusCitation.title} {nexusCitation.version}."
191
+ return {
192
+ # "commit": commitHash,
193
+ "dateDASHreleased": publishedAt,
194
+ "identifiers": [{
195
+ "type": "url",
196
+ "value": releaseHtmlUrl,
197
+ "description": identifierDescription,
198
+ }],
199
+ "repositoryDASHcode": releaseHtmlUrl,
200
+ }
201
+
202
+ def addGitHubRelease(nexusCitation: CitationNexus) -> CitationNexus:
203
+ """
204
+ Update the nexusCitation with GitHub release information.
205
+
206
+ This function populates the following fields on the nexusCitation:
207
+ - commit: using the commit hash from GitHub.
208
+ - dateDASHreleased: the release date.
209
+ - identifiers: appends a GitHub-specific identifier.
210
+ - repositoryDASHcode: the URL to view the commit in the repository.
211
+
212
+ Returns:
213
+ The updated CitationNexus instance.
214
+
215
+ Raises:
216
+ Any exception raised by getGitHubRelease.
217
+ """
218
+ gitHubReleaseData = getGitHubRelease(nexusCitation)
219
+ nexusCitation.commit = gitHubReleaseData.get("commit")
220
+ nexusCitation.dateDASHreleased = gitHubReleaseData.get("dateDASHreleased")
221
+ # Overwrite the existing list of identifiers. This could be better
222
+ nexusCitation.identifiers = gitHubReleaseData.get("identifiers", [])
223
+ nexusCitation.repositoryDASHcode = gitHubReleaseData.get("repositoryDASHcode")
224
+ return nexusCitation
225
+
226
+ def getPyPIrelease(nexusCitation: CitationNexus) -> Dict[str, Any]:
227
+ if not nexusCitation.title:
228
+ raise ValueError("Package name (title) is required to get PyPI release info.")
229
+ if not nexusCitation.version:
230
+ raise ValueError("Package version is required to get PyPI release info.")
231
+
232
+ packageName = packaging.utils.canonicalize_name(nexusCitation.title)
233
+ version = str(nexusCitation.version)
234
+ return {
235
+ "repositoryDASHartifact": f"https://pypi.org/project/{packageName}/{version}/"
236
+ }
237
+
238
+ def addPyPIrelease(nexusCitation: CitationNexus) -> CitationNexus:
239
+ pypiReleaseData = getPyPIrelease(nexusCitation)
240
+ nexusCitation.repositoryDASHartifact = pypiReleaseData.get("repositoryDASHartifact")
241
+ return nexusCitation
242
+
243
+ def getNexusCitation(pathFilenameCitationSSOT: pathlib.Path) -> CitationNexus:
244
+
245
+ # `cffconvert.cli.create_citation.create_citation()` is PAINFULLY mundane, but a major problem
246
+ # in the CFF ecosystem is divergence. Therefore, I will use this function so that my code
247
+ # converges with the CFF ecosystem.
248
+ citationObject: cffconvert.Citation = create_citation(infile=pathFilenameCitationSSOT, url=None)
249
+ # `._parse()` is a yaml loader: use it for convergence
250
+ cffobj: Dict[Any, Any] = citationObject._parse()
251
+
252
+ nexusCitation = CitationNexus(
253
+ cffDASHversion=cffobj["cff-version"],
254
+ message=cffobj["message"],
255
+ )
256
+
257
+ Z0Z_list: List[attrs.Attribute] = list(attrs.fields(type(nexusCitation)))
258
+ for Z0Z_field in Z0Z_list:
259
+ cffobjKeyName: str = Z0Z_field.name.replace("DASH", "-")
260
+ cffobjValue = cffobj.get(cffobjKeyName)
261
+ if cffobjValue: # An empty list will be False
262
+ setattr(nexusCitation, Z0Z_field.name, cffobjValue)
263
+
264
+ nexusCitation = nexusCitation.setInStone("Citation")
265
+ return nexusCitation
266
+
267
+ def getPypaMetadata(packageData: Dict[str, Any]) -> PyPAMetadata:
268
+ """
269
+ Create a PyPA metadata object (version 2.4) from packageData.
270
+ https://packaging.python.org/en/latest/specifications/core-metadata/
271
+ """
272
+ dictionaryProjectURLs: Dict[str, str] = {}
273
+ for urlName, url in packageData.get("urls", {}).items():
274
+ urlName = urlName.lower()
275
+ if urlName in listProjectURLsTarget:
276
+ dictionaryProjectURLs[urlName] = url
277
+
278
+ metadataRaw = packaging.metadata.RawMetadata(
279
+ keywords=packageData.get("keywords", []),
280
+ license_expression=packageData.get("license", {}).get("text", ""),
281
+ metadata_version="2.4",
282
+ name=packaging.utils.canonicalize_name(packageData.get("name", None), validate=True), # packaging.metadata.InvalidMetadata: 'name' is a required field
283
+ project_urls=dictionaryProjectURLs,
284
+ version=packageData.get("version", None),
285
+ )
286
+
287
+ metadata = PyPAMetadata().from_raw(metadataRaw)
288
+ return metadata
289
+
182
290
  def writeCitation(nexusCitation: CitationNexus, pathFilenameCitationSSOT: pathlib.Path, pathFilenameCitationDOTcffRepo: pathlib.Path):
183
291
  # NOTE embarrassingly hacky process to follow
184
292
  parameterIndent= 2
@@ -195,7 +303,7 @@ def writeCitation(nexusCitation: CitationNexus, pathFilenameCitationSSOT: pathli
195
303
  for keyName in list(dictionaryCitation.keys()):
196
304
  dictionaryCitation[keyName.replace("DASH", "-")] = dictionaryCitation.pop(keyName)
197
305
 
198
- pathFilenameForValidation = pathlib.Path(tempfile.mktemp())
306
+ pathFilenameForValidation = pathFilenameCitationSSOT.with_stem('validation')
199
307
 
200
308
  def writeStream(pathFilename):
201
309
  with open(pathFilename, 'w') as pathlibIsAStealthContextManagerThatRuamelCannotDetectAndRefusesToWorkWith:
@@ -204,19 +312,23 @@ def writeCitation(nexusCitation: CitationNexus, pathFilenameCitationSSOT: pathli
204
312
  writeStream(pathFilenameForValidation)
205
313
 
206
314
  citationObject: cffconvert.Citation = create_citation(infile=pathFilenameForValidation, url=None)
207
- if citationObject.validate(verbose=True) is None:
315
+ if citationObject.validate() is None:
208
316
  writeStream(pathFilenameCitationSSOT)
209
317
  writeStream(pathFilenameCitationDOTcffRepo)
210
318
 
319
+ pathFilenameForValidation.unlink()
320
+
211
321
  def logistics():
212
322
  # Prefer reliable, dynamic values over hardcoded ones
213
- packageNameHARDCODED: str = 'mapFolding'
214
-
215
- packageName: str = packageNameHARDCODED
216
323
  pathRepoRoot = pathlib.Path(__file__).parent.parent.parent
217
324
  pathFilenamePackageSSOT = pathRepoRoot / 'pyproject.toml'
218
- filenameGitHubAction = 'updateCitation.yml'
219
- pathFilenameGitHubAction = pathRepoRoot / '.github' / 'workflows' / filenameGitHubAction
325
+
326
+ tomlPackageData: Dict[str, Any] = tomli.loads(pathFilenamePackageSSOT.read_text())['project']
327
+ # https://packaging.python.org/en/latest/specifications/pyproject-toml/
328
+
329
+ packageName: str = tomlPackageData.get("name", None)
330
+ if not packageName:
331
+ raise ValueError("Package name is required.")
220
332
 
221
333
  filenameCitationDOTcff = 'CITATION.cff'
222
334
  pathCitations = pathRepoRoot / packageName / 'citations'
@@ -225,13 +337,17 @@ def logistics():
225
337
 
226
338
  nexusCitation = getNexusCitation(pathFilenameCitationSSOT)
227
339
 
228
- tomlPackageData: Dict[str, Any] = tomli.loads(pathFilenamePackageSSOT.read_text())['project']
229
- # https://packaging.python.org/en/latest/specifications/pyproject-toml/
230
340
  pypaMetadata: PyPAMetadata = getPypaMetadata(tomlPackageData)
231
341
 
232
342
  nexusCitation = addPypaMetadata(nexusCitation, pypaMetadata)
233
343
  nexusCitation = add_pyprojectDOTtoml(nexusCitation, tomlPackageData)
234
344
 
345
+ nexusCitation = addGitHubRelease(nexusCitation)
346
+ nexusCitation = addPyPIrelease(nexusCitation)
347
+
348
+ filenameGitHubAction = 'updateCitation.yml'
349
+ pathFilenameGitHubAction = pathRepoRoot / '.github' / 'workflows' / filenameGitHubAction
350
+
235
351
  writeCitation(nexusCitation, pathFilenameCitationSSOT, pathFilenameCitationDOTcffRepo)
236
352
 
237
353
  if __name__ == '__main__':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mapFolding
3
- Version: 0.3.7
3
+ Version: 0.3.8
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
@@ -0,0 +1,26 @@
1
+ benchmarks/benchmarking.py,sha256=HD_0NSvuabblg94ftDre6LFnXShTe8MYj3hIodW-zV0,3076
2
+ citations/constants.py,sha256=1n3AC_18LOjmMLJWDo8YOGEIvDY7oZyLtI4QwqdB6z4,73
3
+ citations/updateCitation.py,sha256=ihZFafY1TtVgQ1cnVNAHGCJMgoBUR8cm1qvGobwjNKA,14949
4
+ reference/flattened.py,sha256=6blZ2Y9G8mu1F3gV8SKndPE398t2VVFlsgKlyeJ765A,16538
5
+ reference/hunterNumba.py,sha256=HWndRgsajOf76rbb2LDNEZ6itsdYbyV-k3wgOFjeR6c,7104
6
+ reference/irvineJavaPort.py,sha256=Sj-63Z-OsGuDoEBXuxyjRrNmmyl0d7Yz_XuY7I47Oyg,4250
7
+ reference/jax.py,sha256=rojyK80lOATtbzxjGOHWHZngQa47CXCLJHZwIdN2MwI,14955
8
+ reference/lunnan.py,sha256=XEcql_gxvCCghb6Or3qwmPbn4IZUbZTaSmw_fUjRxZE,5037
9
+ reference/lunnanNumpy.py,sha256=HqDgSwTOZA-G0oophOEfc4zs25Mv4yw2aoF1v8miOLk,4653
10
+ reference/lunnanWhile.py,sha256=7NY2IKO5XBgol0aWWF_Fi-7oTL9pvu_z6lB0TF1uVHk,4063
11
+ reference/rotatedEntryPoint.py,sha256=z0QyDQtnMvXNj5ntWzzJUQUMFm1-xHGLVhtYzwmczUI,11530
12
+ reference/total_countPlus1vsPlusN.py,sha256=usenM8Yn_G1dqlPl7NKKkcnbohBZVZBXTQRm2S3_EDA,8106
13
+ someAssemblyRequired/__init__.py,sha256=3JnAKXfaYPtmxV_4AnZ6KpCosT_0GFV5Nw7K8sz4-Uo,34
14
+ someAssemblyRequired/getLLVMforNoReason.py,sha256=FtJzw2pZS3A4NimWdZsegXaU-vKeCw8m67kcfb5wvGM,894
15
+ someAssemblyRequired/makeJob.py,sha256=UUCNdkFnSrlhRAjkQp2_Qv4joKGXRnNJhZ4OdxI_aqU,2628
16
+ someAssemblyRequired/synthesizeModuleJobNumba.py,sha256=TCYNNI19vCw0C1FLP691VPpYknkedj-zpx-xQFVIVJU,9851
17
+ someAssemblyRequired/synthesizeModulesNumba.py,sha256=aOst0EC5zStLbUSDTpyMoyQe_8_3CLX7JSdHdPmlAsY,22847
18
+ syntheticModules/__init__.py,sha256=XMjNt8x24M82i1eCNVcWpIIhXwfVCnjfbb-y36RAafA,131
19
+ syntheticModules/numbaInitialize.py,sha256=H8Y22wwE5kYyroztAJ5HJ2W6qfw7Fu4iVrUsCPmnFjY,4088
20
+ syntheticModules/numbaParallel.py,sha256=fvprhBepBxQkY5yQInYRWb7agDVMArypGC6Y510tAcU,5452
21
+ syntheticModules/numbaSequential.py,sha256=Knru1I7BBvsBDTex7NfxO2U3IEcQfz-uG4Lpw2tLhOI,3629
22
+ mapFolding-0.3.8.dist-info/METADATA,sha256=hgOrdxAEB5vsBdIK41JvUc65iHQladkMBbbXwg-YPnc,7729
23
+ mapFolding-0.3.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
24
+ mapFolding-0.3.8.dist-info/entry_points.txt,sha256=F3OUeZR1XDTpoH7k3wXuRb3KF_kXTTeYhu5AGK1SiOQ,146
25
+ mapFolding-0.3.8.dist-info/top_level.txt,sha256=yVG9dNZywoaddcsUdEDg7o0XOBzJd_4Z-sDaXGHpiMY,69
26
+ mapFolding-0.3.8.dist-info/RECORD,,
@@ -1,25 +1,53 @@
1
- from mapFolding import getPathFilenameFoldsTotal, computationState
2
- from mapFolding import outfitCountFolds
1
+ import importlib.util
2
+ from mapFolding import getPathFilenameFoldsTotal, computationState, outfitCountFolds, getAlgorithmSource
3
3
  from typing import Any, Literal, Optional, Sequence, Type, overload
4
4
  import pathlib
5
5
  import pickle
6
6
 
7
7
  @overload
8
8
  def makeStateJob(listDimensions: Sequence[int], writeJob: Literal[True] = True
9
- , **keywordArguments: Optional[Type[Any]]) -> pathlib.Path:
9
+ , **keywordArguments: Optional[str]) -> pathlib.Path:
10
10
  ...
11
11
 
12
12
  @overload
13
13
  def makeStateJob(listDimensions: Sequence[int], writeJob: Literal[False] = False
14
- , **keywordArguments: Optional[Type[Any]]) -> computationState:
14
+ , **keywordArguments: Optional[str]) -> computationState:
15
15
  ...
16
16
 
17
- def makeStateJob(listDimensions: Sequence[int], writeJob: bool = True, **keywordArguments: Optional[Type[Any]]) -> computationState | pathlib.Path:
17
+ def makeStateJob(listDimensions: Sequence[int], writeJob: bool = True, **keywordArguments: Optional[str]) -> computationState | pathlib.Path:
18
+ """
19
+ Creates a computation state job for map folding calculations and optionally saves it to disk.
20
+
21
+ This function initializes a computation state for map folding calculations based on the given dimensions,
22
+ sets up the initial counting configuration, and can optionally save the state to a pickle file.
23
+
24
+ Parameters
25
+ ----------
26
+ listDimensions : Sequence[int]
27
+ The dimensions of the map to be folded, typically as [height, width].
28
+ writeJob : bool, optional
29
+ If True, saves the computation state to disk. If False, returns the state object directly.
30
+ Default is True.
31
+ **keywordArguments : Optional[str]
32
+ Additional keyword arguments to be passed to the outfitCountFolds function.
33
+
34
+ Returns
35
+ -------
36
+ Union[computationState, pathlib.Path]
37
+ If writeJob is False, returns the computation state object.
38
+ If writeJob is True, returns the Path object pointing to the saved state file.
39
+
40
+ Notes
41
+ -----
42
+ The function creates necessary directories and saves the state as a pickle file
43
+ when writeJob is True. The file is saved in a directory structure based on the map shape.
44
+ """
18
45
 
19
46
  stateUniversal: computationState = outfitCountFolds(listDimensions, computationDivisions=None, CPUlimit=None, **keywordArguments)
20
47
 
21
- from mapFolding.syntheticModules import countInitialize
22
- countInitialize(stateUniversal['connectionGraph'], stateUniversal['gapsWhere'], stateUniversal['my'], stateUniversal['track'])
48
+
49
+ moduleSource = getAlgorithmSource()
50
+ moduleSource.countInitialize(stateUniversal['connectionGraph'], stateUniversal['gapsWhere'], stateUniversal['my'], stateUniversal['track'])
23
51
 
24
52
  if not writeJob:
25
53
  return stateUniversal
@@ -1,5 +1,5 @@
1
1
  from mapFolding import getPathFilenameFoldsTotal, indexMy, indexTrack
2
- from mapFolding import make_dtype, datatypeLargeDEFAULT, datatypeMediumDEFAULT, datatypeSmallDEFAULT, datatypeModuleDEFAULT
2
+ from mapFolding import setDatatypeElephino, setDatatypeFoldsTotal, setDatatypeLeavesTotal, setDatatypeModule, hackSSOTdatatype
3
3
  from someAssemblyRequired import makeStateJob
4
4
  from typing import Optional
5
5
  import importlib
@@ -13,9 +13,36 @@ import python_minifier
13
13
  identifierCallableLaunch = "goGoGadgetAbsurdity"
14
14
 
15
15
  def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: str) -> str:
16
- def process_nested_array(arraySlice):
16
+ """Converts a NumPy array into a compressed string representation using run-length encoding (RLE).
17
+
18
+ This function takes a NumPy array and converts it into an optimized string representation by:
19
+ 1. Compressing consecutive sequences of numbers into range objects
20
+ 2. Minimizing repeated zeros using array multiplication syntax
21
+ 3. Converting the result into a valid Python array initialization statement
22
+
23
+ Parameters:
24
+ arrayTarget (numpy.ndarray): The input NumPy array to be converted
25
+ identifierName (str): The variable name to use in the output string
26
+
27
+ Returns:
28
+ str: A string containing Python code that recreates the input array in compressed form.
29
+ Format: "{identifierName} = numpy.array({compressed_data}, dtype=numpy.{dtype})"
30
+
31
+ Example:
32
+ >>> arr = numpy.array([[0,0,0,1,2,3,4,0,0]])
33
+ >>> print(makeStrRLEcompacted(arr, "myArray"))
34
+ "myArray = numpy.array([[0]*3,*range(1,5),[0]*2], dtype=numpy.int64)"
35
+
36
+ Notes:
37
+ - Sequences of 4 or fewer numbers are kept as individual values
38
+ - Sequences longer than 4 numbers are converted to range objects
39
+ - Consecutive zeros are compressed using multiplication syntax
40
+ - The function preserves the original array's dtype
41
+ """
42
+
43
+ def compressRangesNDArrayNoFlatten(arraySlice):
17
44
  if isinstance(arraySlice, numpy.ndarray) and arraySlice.ndim > 1:
18
- return [process_nested_array(arraySlice[index]) for index in range(arraySlice.shape[0])]
45
+ return [compressRangesNDArrayNoFlatten(arraySlice[index]) for index in range(arraySlice.shape[0])]
19
46
  elif isinstance(arraySlice, numpy.ndarray) and arraySlice.ndim == 1:
20
47
  listWithRanges = []
21
48
  for group in more_itertools.consecutive_groups(arraySlice.tolist()):
@@ -28,7 +55,7 @@ def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: str) -> str:
28
55
  return listWithRanges
29
56
  return arraySlice
30
57
 
31
- arrayAsNestedLists = process_nested_array(arrayTarget)
58
+ arrayAsNestedLists = compressRangesNDArrayNoFlatten(arrayTarget)
32
59
 
33
60
  stringMinimized = python_minifier.minify(str(arrayAsNestedLists))
34
61
  commaZeroMaximum = arrayTarget.shape[-1] - 1
@@ -40,25 +67,47 @@ def makeStrRLEcompacted(arrayTarget: numpy.ndarray, identifierName: str) -> str:
40
67
 
41
68
  return f"{identifierName} = numpy.array({stringMinimized}, dtype=numpy.{arrayTarget.dtype})"
42
69
 
43
- def writeModuleWithNumba(listDimensions, **keywordArguments: Optional[str]) -> pathlib.Path:
44
- datatypeLarge = keywordArguments.get('datatypeLarge', datatypeLargeDEFAULT)
45
- datatypeMedium = keywordArguments.get('datatypeMedium', datatypeMediumDEFAULT)
46
- datatypeSmall = keywordArguments.get('datatypeSmall', datatypeSmallDEFAULT)
47
- datatypeModule = keywordArguments.get('datatypeModule', datatypeModuleDEFAULT)
48
-
49
- dtypeLarge = make_dtype(datatypeLarge, datatypeModule) # type: ignore
50
- dtypeMedium = make_dtype(datatypeMedium, datatypeModule) # type: ignore
51
- dtypeSmall = make_dtype(datatypeSmall, datatypeModule) # type: ignore
52
-
53
- stateJob = makeStateJob(listDimensions, writeJob=False, dtypeLarge = dtypeLarge, dtypeMedium = dtypeMedium, dtypeSmall = dtypeSmall)
70
+ def writeModuleWithNumba(listDimensions) -> pathlib.Path:
71
+ """
72
+ Writes a Numba-optimized Python module for map folding calculations.
73
+
74
+ This function takes map dimensions and generates a specialized Python module with Numba
75
+ optimizations. It processes a sequential counting algorithm, adds Numba decorators and
76
+ necessary data structures, and writes the resulting code to a file.
77
+
78
+ Parameters:
79
+ listDimensions: List of integers representing the dimensions of the map to be folded.
80
+
81
+ Returns:
82
+ pathlib.Path: Path to the generated Python module file.
83
+
84
+ The generated module includes:
85
+ - Numba JIT compilation decorators for performance optimization
86
+ - Required numpy and numba imports
87
+ - Dynamic and static data structures needed for folding calculations
88
+ - Processed algorithm from the original sequential counter
89
+ - Launch code for standalone execution
90
+ - Code to write the final fold count to a file
91
+ The function handles:
92
+ - Translation of original code to Numba-compatible syntax
93
+ - Insertion of pre-calculated values from the state job
94
+ - Management of variable declarations and assignments
95
+ - Setup of proper data types for Numba optimization
96
+ - Organization of the output file structure
97
+
98
+ Note:
99
+ The generated module requires Numba and numpy to be installed.
100
+ The output file will be placed in the same directory as the folds total file,
101
+ with a .py extension.
102
+ """
103
+ stateJob = makeStateJob(listDimensions, writeJob=False)
54
104
  pathFilenameFoldsTotal = getPathFilenameFoldsTotal(stateJob['mapShape'])
55
105
 
56
106
  from syntheticModules import countSequential
57
107
  algorithmSource = countSequential
58
108
  codeSource = inspect.getsource(algorithmSource)
59
109
 
60
- if datatypeLarge:
61
- lineNumba = f"@numba.jit(numba.types.{datatypeLarge}(), cache=True, nopython=True, fastmath=True, forceinline=True, inline='always', looplift=False, _nrt=True, error_model='numpy', parallel=False, boundscheck=False, no_cfunc_wrapper=True, no_cpython_wrapper=False)"
110
+ lineNumba = f"@numba.jit(numba.types.{hackSSOTdatatype('datatypeFoldsTotal')}(), cache=True, nopython=True, fastmath=True, forceinline=True, inline='always', looplift=False, _nrt=True, error_model='numpy', parallel=False, boundscheck=False, no_cfunc_wrapper=False, no_cpython_wrapper=False)"
62
111
 
63
112
  linesImport = "\n".join([
64
113
  "import numpy"
@@ -68,8 +117,6 @@ def writeModuleWithNumba(listDimensions, **keywordArguments: Optional[str]) -> p
68
117
  ImaIndent = ' '
69
118
  linesDataDynamic = """"""
70
119
  linesDataDynamic = "\n".join([linesDataDynamic
71
- # , ImaIndent + f"foldsTotal = numba.types.{datatypeLarge}(0)"
72
- # , ImaIndent + makeStrRLEcompacted(stateJob['foldGroups'], 'foldGroups')
73
120
  , ImaIndent + makeStrRLEcompacted(stateJob['gapsWhere'], 'gapsWhere')
74
121
  ])
75
122
 
@@ -97,11 +144,18 @@ def writeModuleWithNumba(listDimensions, **keywordArguments: Optional[str]) -> p
97
144
  elif 'my[indexMy.' in lineSource:
98
145
  if 'dimensionsTotal' in lineSource:
99
146
  continue
100
- # leaf1ndex = my[indexMy.leaf1ndex.value]
147
+ # Statements are in the form: leaf1ndex = my[indexMy.leaf1ndex.value]
101
148
  identifier, statement = lineSource.split('=')
102
- lineSource = ImaIndent + identifier.strip() + f"=numba.types.{datatypeSmall}({str(eval(statement.strip()))})"
149
+ lineSource = ImaIndent + identifier.strip() + f"=numba.types.{hackSSOTdatatype(identifier.strip())}({str(eval(statement.strip()))})"
150
+ elif ': int =' in lineSource or ':int=' in lineSource:
151
+ if 'dimensionsTotal' in lineSource:
152
+ continue
153
+ # Statements are in the form: groupsOfFolds: int = 0
154
+ assignment, statement = lineSource.split('=')
155
+ identifier = assignment.split(':')[0].strip()
156
+ lineSource = ImaIndent + identifier.strip() + f"=numba.types.{hackSSOTdatatype(identifier.strip())}({str(eval(statement.strip()))})"
103
157
  elif 'track[indexTrack.' in lineSource:
104
- # leafAbove = track[indexTrack.leafAbove.value]
158
+ # Statements are in the form: leafAbove = track[indexTrack.leafAbove.value]
105
159
  identifier, statement = lineSource.split('=')
106
160
  lineSource = ImaIndent + makeStrRLEcompacted(eval(statement.strip()), identifier.strip())
107
161
  elif 'foldGroups[-1]' in lineSource:
@@ -144,11 +198,11 @@ if __name__ == '__main__':
144
198
  return pathFilenameDestination
145
199
 
146
200
  if __name__ == '__main__':
147
- listDimensions = [6,6]
148
- datatypeLarge = 'int64'
149
- datatypeMedium = 'uint8'
150
- datatypeSmall = datatypeMedium
151
- pathFilenameModule = writeModuleWithNumba(listDimensions, datatypeLarge=datatypeLarge, datatypeMedium=datatypeMedium, datatypeSmall=datatypeSmall)
201
+ listDimensions = [5,5]
202
+ setDatatypeFoldsTotal('int64', sourGrapes=True)
203
+ setDatatypeElephino('uint8', sourGrapes=True)
204
+ setDatatypeLeavesTotal('int8', sourGrapes=True)
205
+ pathFilenameModule = writeModuleWithNumba(listDimensions)
152
206
 
153
207
  # Induce numba.jit compilation
154
208
  moduleSpec = importlib.util.spec_from_file_location(pathFilenameModule.stem, pathFilenameModule)