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+
2323import platformdirs
24+ import requests
25+ import semver
2426
2527
2628@functools .cache
@@ -89,15 +91,19 @@ def version_string(path=None, *, valid_semver=False):
8991 tag = subprocess .run (
9092 "git describe --tags --exact-match" ,
9193 shell = True ,
92- stdout = subprocess .PIPE ,
93- stderr = subprocess .PIPE ,
94+ capture_output = True ,
9495 cwd = path ,
96+ check = False , # error handled below
9597 )
9698 if tag .returncode == 0 :
9799 version = tag .stdout .strip ().decode ("utf-8" , "strict" )
98100 else :
99101 describe = subprocess .run (
100- "git describe --tags --always" , shell = True , stdout = subprocess .PIPE , cwd = path
102+ "git describe --tags --always" ,
103+ shell = True ,
104+ stdout = subprocess .PIPE ,
105+ cwd = path ,
106+ check = True , # Let exception propagate an error from git
101107 )
102108 describe = describe .stdout .strip ().decode ("utf-8" , "strict" ).rsplit ("-" , maxsplit = 2 )
103109 if len (describe ) == 3 :
@@ -106,7 +112,11 @@ def version_string(path=None, *, valid_semver=False):
106112 else :
107113 tag = "0.0.0"
108114 commit_count = subprocess .run (
109- "git rev-list --count HEAD" , shell = True , stdout = subprocess .PIPE , cwd = path
115+ "git rev-list --count HEAD" ,
116+ shell = True ,
117+ stdout = subprocess .PIPE ,
118+ cwd = path ,
119+ check = True , # Let exception propagate an error from git
110120 )
111121 additional_commits = commit_count .stdout .strip ().decode ("utf-8" , "strict" )
112122 commitish = describe [0 ]
@@ -135,17 +145,21 @@ def mpy_cross(version, quiet=False):
135145 # Try to pull from S3
136146 uname = platform .uname ()
137147 s3_url = None
138- if uname [0 ].title () == "Linux" and uname [4 ].lower () in ( "amd64" , "x86_64" ) :
148+ if uname [0 ].title () == "Linux" and uname [4 ].lower () in { "amd64" , "x86_64" } :
139149 s3_url = f"{ S3_MPY_PREFIX } /linux-amd64/mpy-cross-linux-amd64-{ circuitpython_tag } .static"
140150 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"
151+ s3_url = (
152+ f"{ S3_MPY_PREFIX } /linux-raspbian/mpy-cross-linux-raspbian-{ circuitpython_tag } ."
153+ "static-raspbian"
154+ )
142155 elif uname [0 ].title () == "Darwin" :
143156 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" ) :
157+ elif uname [0 ].title () == "Windows" and uname [4 ].lower () in { "amd64" , "x86_64" } :
145158 s3_url = f"{ S3_MPY_PREFIX } /windows/mpy-cross-windows-{ circuitpython_tag } .static.exe"
146159 elif not quiet :
147160 print (
148- f"Pre-built mpy-cross not available for sysname='{ uname [0 ]} ' release='{ uname [2 ]} ' machine='{ uname [4 ]} '."
161+ "Pre-built mpy-cross not available for" ,
162+ f"sysname='{ uname [0 ]} ' release='{ uname [2 ]} ' machine='{ uname [4 ]} '." ,
149163 )
150164
151165 if s3_url is not None :
@@ -201,13 +215,13 @@ def mpy_cross(version, quiet=False):
201215
202216
203217def _munge_to_temp (original_path , temp_file , library_version ):
204- with open (original_path , "r" , encoding = "utf-8" ) as original_file :
218+ with open (original_path , encoding = "utf-8" ) as original_file :
205219 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 )
220+ ln = line .strip ("\n " )
221+ if ln .startswith ("__version__" ):
222+ ln = ln .replace ("0.0.0-auto.0" , library_version )
223+ ln = ln .replace ("0.0.0+auto.0" , library_version )
224+ print (ln , file = temp_file )
211225 temp_file .flush ()
212226
213227
@@ -230,7 +244,8 @@ def get_package_info(library_path, package_folder_prefix):
230244
231245 if blocklisted :
232246 print (
233- f"{ lib_path } /settings.toml:1: { blocklisted [0 ]} blocklisted: not using metadata from pyproject.toml"
247+ f"{ lib_path } /settings.toml:1: { blocklisted [0 ]} " ,
248+ "blocklisted: not using metadata from pyproject.toml" ,
234249 )
235250 py_modules = packages = ()
236251
@@ -241,7 +256,7 @@ def get_package_info(library_path, package_folder_prefix):
241256 if packages and py_modules :
242257 raise ValueError ("Cannot specify both tool.setuptools.py-modules and .packages" )
243258
244- elif packages :
259+ if packages :
245260 if len (packages ) > 1 :
246261 raise ValueError ("Only a single package is supported" )
247262 package_name = packages [0 ]
@@ -263,29 +278,16 @@ def get_package_info(library_path, package_folder_prefix):
263278 py_files = [lib_path / f"{ py_module } .py" ]
264279
265280 else :
266- 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
281+ _detect_legacy_package_structure (
282+ package_info ,
283+ package_files ,
284+ py_files ,
285+ glob_search ,
286+ parent_idx ,
287+ package_folder_prefix ,
288+ lib_path ,
289+ library_path ,
290+ )
289291
290292 if len (py_files ) > 1 :
291293 raise ValueError (
@@ -307,6 +309,41 @@ def get_package_info(library_path, package_folder_prefix):
307309 return package_info
308310
309311
312+ def _detect_legacy_package_structure (
313+ package_info : dict ,
314+ package_files : list [pathlib .Path ],
315+ py_files : list [pathlib .Path ],
316+ glob_search : list [pathlib .Path ],
317+ parent_idx : int ,
318+ package_folder_prefix : str ,
319+ lib_path : pathlib .Path ,
320+ library_path : pathlib .Path ,
321+ ) -> None :
322+ print (f"{ lib_path } : Using legacy autodetection" )
323+ package_info ["is_package" ] = False
324+ for file in glob_search :
325+ if file .parts [parent_idx ] != "examples" :
326+ if len (file .parts ) > parent_idx + 1 :
327+ for prefix in package_folder_prefix :
328+ if file .parts [parent_idx ].startswith (prefix ):
329+ package_info ["is_package" ] = True
330+ if package_info ["is_package" ]:
331+ package_files .append (file )
332+ else :
333+ if file .name in IGNORE_PY :
334+ # print("Ignoring:", file.resolve())
335+ continue
336+ if file .parent == lib_path :
337+ py_files .append (file )
338+
339+ if package_files :
340+ package_info ["module_name" ] = package_files [0 ].relative_to (library_path ).parent .name
341+ elif py_files :
342+ package_info ["module_name" ] = py_files [0 ].relative_to (library_path ).name [:- 3 ]
343+ else :
344+ package_info ["module_name" ] = None
345+
346+
310347def library (
311348 library_path , output_directory , package_folder_prefix , mpy_cross = None , example_bundle = False
312349):
@@ -327,33 +364,9 @@ def library(
327364 for filename in py_package_files :
328365 full_path = os .path .join (library_path , filename )
329366 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-
367+ _run_mpy_cross_on_mod (
368+ filename , full_path , output_file , mpy_cross , library_path , library_version
369+ )
357370 requirements_files = lib_path .glob ("requirements.txt*" )
358371 requirements_files = [f for f in requirements_files if f .stat ().st_size > 0 ]
359372
@@ -383,3 +396,39 @@ def library(
383396
384397 os .makedirs (os .path .join (* output_file .split (os .path .sep )[:- 1 ]), exist_ok = True )
385398 shutil .copyfile (full_path , output_file )
399+
400+
401+ def _run_mpy_cross_on_mod (
402+ filename : pathlib .Path ,
403+ full_path : str ,
404+ output_file : str ,
405+ mpy_cross : str ,
406+ library_path : str ,
407+ library_version : str ,
408+ ) -> None :
409+ if filename .suffix == ".py" :
410+ with tempfile .NamedTemporaryFile (delete = False , mode = "w+" ) as temp_file :
411+ temp_file_name = temp_file .name
412+ try :
413+ _munge_to_temp (full_path , temp_file , library_version )
414+ temp_file .close ()
415+ if mpy_cross and os .stat (temp_file .name ).st_size != 0 :
416+ output_file = output_file .with_suffix (".mpy" )
417+ mpy_success = subprocess .call (
418+ [
419+ mpy_cross ,
420+ "-o" ,
421+ output_file ,
422+ "-s" ,
423+ str (filename .relative_to (library_path )),
424+ temp_file .name ,
425+ ]
426+ )
427+ if mpy_success != 0 :
428+ raise RuntimeError ("mpy-cross failed on" , full_path )
429+ else :
430+ shutil .copyfile (temp_file_name , output_file )
431+ finally :
432+ os .remove (temp_file_name )
433+ else :
434+ shutil .copyfile (full_path , output_file )
0 commit comments