1010import multiprocessing
1111import os
1212import os .path
13- import platform
1413import pathlib
14+ import platform
1515import re
16- import requests
17- import semver
1816import shutil
1917import stat
20- import sys
2118import subprocess
19+ import sys
2220import tempfile
21+ from fileinput import filename
22+ from typing import Optional
23+
2324import platformdirs
25+ import requests
26+ import semver
2427
2528
2629@functools .cache
@@ -89,15 +92,19 @@ def version_string(path=None, *, valid_semver=False):
8992 tag = subprocess .run (
9093 "git describe --tags --exact-match" ,
9194 shell = True ,
92- stdout = subprocess .PIPE ,
93- stderr = subprocess .PIPE ,
95+ capture_output = True ,
9496 cwd = path ,
97+ check = False , # error handled below
9598 )
9699 if tag .returncode == 0 :
97100 version = tag .stdout .strip ().decode ("utf-8" , "strict" )
98101 else :
99102 describe = subprocess .run (
100- "git describe --tags --always" , shell = True , stdout = subprocess .PIPE , cwd = path
103+ "git describe --tags --always" ,
104+ shell = True ,
105+ stdout = subprocess .PIPE ,
106+ cwd = path ,
107+ check = True , # Let exception propagate an error from git
101108 )
102109 describe = describe .stdout .strip ().decode ("utf-8" , "strict" ).rsplit ("-" , maxsplit = 2 )
103110 if len (describe ) == 3 :
@@ -106,7 +113,11 @@ def version_string(path=None, *, valid_semver=False):
106113 else :
107114 tag = "0.0.0"
108115 commit_count = subprocess .run (
109- "git rev-list --count HEAD" , shell = True , stdout = subprocess .PIPE , cwd = path
116+ "git rev-list --count HEAD" ,
117+ shell = True ,
118+ stdout = subprocess .PIPE ,
119+ cwd = path ,
120+ check = True , # Let exception propagate an error from git
110121 )
111122 additional_commits = commit_count .stdout .strip ().decode ("utf-8" , "strict" )
112123 commitish = describe [0 ]
@@ -135,17 +146,21 @@ def mpy_cross(version, quiet=False):
135146 # Try to pull from S3
136147 uname = platform .uname ()
137148 s3_url = None
138- if uname [0 ].title () == "Linux" and uname [4 ].lower () in ( "amd64" , "x86_64" ) :
149+ if uname [0 ].title () == "Linux" and uname [4 ].lower () in { "amd64" , "x86_64" } :
139150 s3_url = f"{ S3_MPY_PREFIX } /linux-amd64/mpy-cross-linux-amd64-{ circuitpython_tag } .static"
140151 elif uname [0 ].title () == "Linux" and uname [4 ].lower () == "armv7l" :
141- s3_url = f"{ S3_MPY_PREFIX } /linux-raspbian/mpy-cross-linux-raspbian-{ circuitpython_tag } .static-raspbian"
152+ s3_url = (
153+ f"{ S3_MPY_PREFIX } /linux-raspbian/mpy-cross-linux-raspbian-{ circuitpython_tag } ."
154+ "static-raspbian"
155+ )
142156 elif uname [0 ].title () == "Darwin" :
143157 s3_url = f"{ S3_MPY_PREFIX } /macos/mpy-cross-macos-{ circuitpython_tag } -universal"
144- elif uname [0 ].title () == "Windows" and uname [4 ].lower () in ( "amd64" , "x86_64" ) :
158+ elif uname [0 ].title () == "Windows" and uname [4 ].lower () in { "amd64" , "x86_64" } :
145159 s3_url = f"{ S3_MPY_PREFIX } /windows/mpy-cross-windows-{ circuitpython_tag } .static.exe"
146160 elif not quiet :
147161 print (
148- f"Pre-built mpy-cross not available for sysname='{ uname [0 ]} ' release='{ uname [2 ]} ' machine='{ uname [4 ]} '."
162+ "Pre-built mpy-cross not available for" ,
163+ f"sysname='{ uname [0 ]} ' release='{ uname [2 ]} ' machine='{ uname [4 ]} '." ,
149164 )
150165
151166 if s3_url is not None :
@@ -201,13 +216,13 @@ def mpy_cross(version, quiet=False):
201216
202217
203218def _munge_to_temp (original_path , temp_file , library_version ):
204- with open (original_path , "r" , encoding = "utf-8" ) as original_file :
219+ with open (original_path , encoding = "utf-8" ) as original_file :
205220 for line in original_file :
206- line = line .strip ("\n " )
207- if line .startswith ("__version__" ):
208- line = line .replace ("0.0.0-auto.0" , library_version )
209- line = line .replace ("0.0.0+auto.0" , library_version )
210- print (line , file = temp_file )
221+ ln = line .strip ("\n " )
222+ if ln .startswith ("__version__" ):
223+ ln = ln .replace ("0.0.0-auto.0" , library_version )
224+ ln = ln .replace ("0.0.0+auto.0" , library_version )
225+ print (ln , file = temp_file )
211226 temp_file .flush ()
212227
213228
@@ -230,7 +245,8 @@ def get_package_info(library_path, package_folder_prefix):
230245
231246 if blocklisted :
232247 print (
233- f"{ lib_path } /settings.toml:1: { blocklisted [0 ]} blocklisted: not using metadata from pyproject.toml"
248+ f"{ lib_path } /settings.toml:1: { blocklisted [0 ]} " ,
249+ "blocklisted: not using metadata from pyproject.toml" ,
234250 )
235251 py_modules = packages = ()
236252
@@ -241,7 +257,7 @@ def get_package_info(library_path, package_folder_prefix):
241257 if packages and py_modules :
242258 raise ValueError ("Cannot specify both tool.setuptools.py-modules and .packages" )
243259
244- elif packages :
260+ if packages :
245261 if len (packages ) > 1 :
246262 raise ValueError ("Only a single package is supported" )
247263 package_name = packages [0 ]
@@ -264,28 +280,16 @@ def get_package_info(library_path, package_folder_prefix):
264280
265281 else :
266282 print (f"{ lib_path } : Using legacy autodetection" )
267- package_info ["is_package" ] = False
268- for file in glob_search :
269- if file .parts [parent_idx ] != "examples" :
270- if len (file .parts ) > parent_idx + 1 :
271- for prefix in package_folder_prefix :
272- if file .parts [parent_idx ].startswith (prefix ):
273- package_info ["is_package" ] = True
274- if package_info ["is_package" ]:
275- package_files .append (file )
276- else :
277- if file .name in IGNORE_PY :
278- # print("Ignoring:", file.resolve())
279- continue
280- if file .parent == lib_path :
281- py_files .append (file )
282-
283- if package_files :
284- package_info ["module_name" ] = package_files [0 ].relative_to (library_path ).parent .name
285- elif py_files :
286- package_info ["module_name" ] = py_files [0 ].relative_to (library_path ).name [:- 3 ]
287- else :
288- package_info ["module_name" ] = None
283+ _detect_legacy_package_structure (
284+ package_info ,
285+ package_files ,
286+ py_files ,
287+ glob_search ,
288+ parent_idx ,
289+ package_folder_prefix ,
290+ lib_path ,
291+ library_path ,
292+ )
289293
290294 if len (py_files ) > 1 :
291295 raise ValueError (
@@ -307,6 +311,40 @@ def get_package_info(library_path, package_folder_prefix):
307311 return package_info
308312
309313
314+ def _detect_legacy_package_structure (
315+ package_info : dict ,
316+ package_files : list [pathlib .Path ],
317+ py_files : list [pathlib .Path ],
318+ glob_search : list [pathlib .Path ],
319+ parent_idx : int ,
320+ package_folder_prefix : str ,
321+ lib_path : pathlib .Path ,
322+ library_path : str ,
323+ ) -> None :
324+ package_info ["is_package" ] = False
325+ for file in glob_search :
326+ if file .parts [parent_idx ] != "examples" :
327+ if len (file .parts ) > parent_idx + 1 :
328+ for prefix in package_folder_prefix :
329+ if file .parts [parent_idx ].startswith (prefix ):
330+ package_info ["is_package" ] = True
331+ if package_info ["is_package" ]:
332+ package_files .append (file )
333+ else :
334+ if file .name in IGNORE_PY :
335+ # print("Ignoring:", file.resolve())
336+ continue
337+ if file .parent == lib_path :
338+ py_files .append (file )
339+
340+ if package_files :
341+ package_info ["module_name" ] = package_files [0 ].relative_to (library_path ).parent .name
342+ elif py_files :
343+ package_info ["module_name" ] = py_files [0 ].relative_to (library_path ).name [:- 3 ]
344+ else :
345+ package_info ["module_name" ] = None
346+
347+
310348def library (
311349 library_path , output_directory , package_folder_prefix , mpy_cross = None , example_bundle = False
312350):
@@ -327,33 +365,9 @@ def library(
327365 for filename in py_package_files :
328366 full_path = os .path .join (library_path , filename )
329367 output_file = output_directory / filename .relative_to (library_path )
330- if filename .suffix == ".py" :
331- with tempfile .NamedTemporaryFile (delete = False , mode = "w+" ) as temp_file :
332- temp_file_name = temp_file .name
333- try :
334- _munge_to_temp (full_path , temp_file , library_version )
335- temp_file .close ()
336- if mpy_cross and os .stat (temp_file .name ).st_size != 0 :
337- output_file = output_file .with_suffix (".mpy" )
338- mpy_success = subprocess .call (
339- [
340- mpy_cross ,
341- "-o" ,
342- output_file ,
343- "-s" ,
344- str (filename .relative_to (library_path )),
345- temp_file .name ,
346- ]
347- )
348- if mpy_success != 0 :
349- raise RuntimeError ("mpy-cross failed on" , full_path )
350- else :
351- shutil .copyfile (temp_file_name , output_file )
352- finally :
353- os .remove (temp_file_name )
354- else :
355- shutil .copyfile (full_path , output_file )
356-
368+ _run_mpy_cross_on_mod (
369+ filename , full_path , output_file , mpy_cross , library_path , library_version
370+ )
357371 requirements_files = lib_path .glob ("requirements.txt*" )
358372 requirements_files = [f for f in requirements_files if f .stat ().st_size > 0 ]
359373
@@ -383,3 +397,39 @@ def library(
383397
384398 os .makedirs (os .path .join (* output_file .split (os .path .sep )[:- 1 ]), exist_ok = True )
385399 shutil .copyfile (full_path , output_file )
400+
401+
402+ def _run_mpy_cross_on_mod (
403+ filename : pathlib .Path ,
404+ full_path : str ,
405+ output_file : str ,
406+ mpy_cross : pathlib .Path | None ,
407+ library_path : str ,
408+ library_version : str ,
409+ ) -> None :
410+ if filename .suffix == ".py" :
411+ with tempfile .NamedTemporaryFile (delete = False , mode = "w+" ) as temp_file :
412+ temp_file_name = temp_file .name
413+ try :
414+ _munge_to_temp (full_path , temp_file , library_version )
415+ temp_file .close ()
416+ if mpy_cross and os .stat (temp_file .name ).st_size != 0 :
417+ output_file = output_file .with_suffix (".mpy" )
418+ mpy_success = subprocess .call (
419+ [
420+ mpy_cross ,
421+ "-o" ,
422+ output_file ,
423+ "-s" ,
424+ str (filename .relative_to (library_path )),
425+ temp_file .name ,
426+ ]
427+ )
428+ if mpy_success != 0 :
429+ raise RuntimeError ("mpy-cross failed on" , full_path )
430+ else :
431+ shutil .copyfile (temp_file_name , output_file )
432+ finally :
433+ os .remove (temp_file_name )
434+ else :
435+ shutil .copyfile (full_path , output_file )
0 commit comments