66import shutil
77import tempfile
88import zipfile
9+ from concurrent .futures import ThreadPoolExecutor
10+ from multiprocessing import cpu_count
911from pathlib import Path
1012from typing import Union
1113
1214import yaml
1315
16+ from ..._artifacts ._constants import PROCESSES_PER_CORE
1417from ..._utils ._asset_utils import IgnoreFile , traverse_directory
18+ from ..._utils .utils import is_concurrent_component_registration_enabled , is_private_preview_enabled
1519from ...entities ._util import _general_copy
1620from ...entities ._validation import MutableValidationResult , _ValidationResultBuilder
1721from ._artifact_cache import ArtifactCache
@@ -269,6 +273,50 @@ def _artifact_validate_result(self):
269273 self ._load_artifact_additional_includes ()
270274 return self .__artifact_validate_result
271275
276+ @classmethod
277+ def merge_local_path_to_additional_includes (cls , local_path , config_info , conflict_files ):
278+ file_name = Path (local_path ).name
279+ conflicts = conflict_files .get (file_name , set ())
280+ conflicts .add (config_info )
281+ conflict_files [file_name ] = conflicts
282+
283+ @classmethod
284+ def _get_artifacts_by_config (cls , artifact_config ):
285+ artifact_cache = ArtifactCache ()
286+ if any (item not in artifact_config for item in ["feed" , "name" , "version" ]):
287+ raise RuntimeError ("Feed, name and version are required for artifacts config." )
288+ return artifact_cache .get (
289+ organization = artifact_config .get ("organization" , None ),
290+ project = artifact_config .get ("project" , None ),
291+ feed = artifact_config ["feed" ],
292+ name = artifact_config ["name" ],
293+ version = artifact_config ["version" ],
294+ scope = artifact_config .get ("scope" , "organization" ),
295+ resolve = True ,
296+ )
297+
298+ def _resolve_additional_include_config (self , additional_include_config ):
299+ result = []
300+ if isinstance (additional_include_config , dict ) and additional_include_config .get ("type" ) == ARTIFACT_KEY :
301+ try :
302+ # Get the artifacts package from devops to the local
303+ artifact_path = self ._get_artifacts_by_config (additional_include_config )
304+ for item in os .listdir (artifact_path ):
305+ config_info = (
306+ f"{ additional_include_config ['name' ]} :{ additional_include_config ['version' ]} in "
307+ f"{ additional_include_config ['feed' ]} "
308+ )
309+ result .append ((os .path .join (artifact_path , item ), config_info ))
310+ except Exception as e : # pylint: disable=broad-except
311+ self ._artifact_validate_result .append_error (message = e .args [0 ])
312+ elif isinstance (additional_include_config , str ):
313+ result .append ((additional_include_config , additional_include_config ))
314+ else :
315+ self ._artifact_validate_result .append_error (
316+ message = f"Unexpected format in additional_includes, { additional_include_config } "
317+ )
318+ return result
319+
272320 def _load_artifact_additional_includes (self ):
273321 """
274322 Load the additional includes by yaml format.
@@ -290,57 +338,34 @@ def _load_artifact_additional_includes(self):
290338 :return additional_includes: Path list of additional_includes
291339 :rtype additional_includes: List[str]
292340 """
293- additional_includes , conflict_files = [], {}
294341 self .__artifact_validate_result = _ValidationResultBuilder .success ()
295342
296- def merge_local_path_to_additional_includes (local_path , config_info ):
297- additional_includes .append (local_path )
298- file_name = Path (local_path ).name
299- conflicts = conflict_files .get (file_name , set ())
300- conflicts .add (config_info )
301- conflict_files [file_name ] = conflicts
302-
303- def get_artifacts_by_config (artifact_config ):
304- artifact_cache = ArtifactCache ()
305- if any (item not in artifact_config for item in ["feed" , "name" , "version" ]):
306- raise RuntimeError ("Feed, name and version are required for artifacts config." )
307- artifact_path = artifact_cache .get (
308- organization = artifact_config .get ("organization" , None ),
309- project = artifact_config .get ("project" , None ),
310- feed = artifact_config ["feed" ],
311- name = artifact_config ["name" ],
312- version = artifact_config ["version" ],
313- scope = artifact_config .get ("scope" , "organization" ),
314- resolve = True ,
315- )
316- return artifact_path
317-
318343 # Load the artifacts config from additional_includes
319344 with open (self ._additional_includes_file_path ) as f :
320345 additional_includes_configs = yaml .safe_load (f )
321346 additional_includes_configs = additional_includes_configs .get (ADDITIONAL_INCLUDES_KEY , [])
322347
323- for additional_include in additional_includes_configs :
324- if isinstance (additional_include , dict ) and additional_include .get ("type" ) == ARTIFACT_KEY :
325- try :
326- # Get the artifacts package from devops to the local
327- artifact_path = get_artifacts_by_config (additional_include )
328- for item in os .listdir (artifact_path ):
329- config_info = (
330- f"{ additional_include ['name' ]} :{ additional_include ['version' ]} in "
331- f"{ additional_include ['feed' ]} "
332- )
333- merge_local_path_to_additional_includes (
334- local_path = os .path .join (artifact_path , item ), config_info = config_info
348+ additional_includes , conflict_files = [], {}
349+ num_threads = int (cpu_count ()) * PROCESSES_PER_CORE
350+ if (
351+ len (additional_includes_configs ) > 1
352+ and is_concurrent_component_registration_enabled ()
353+ and is_private_preview_enabled ()
354+ ):
355+ with ThreadPoolExecutor (max_workers = num_threads ) as executor :
356+ for result in executor .map (self ._resolve_additional_include_config , additional_includes_configs ):
357+ for local_path , config_info in result :
358+ additional_includes .append (local_path )
359+ self .merge_local_path_to_additional_includes (
360+ local_path = local_path , config_info = config_info , conflict_files = conflict_files
335361 )
336- except Exception as e : # pylint: disable=broad-except
337- self ._artifact_validate_result .append_error (message = e .args [0 ])
338- elif isinstance (additional_include , str ):
339- merge_local_path_to_additional_includes (local_path = additional_include , config_info = additional_include )
340- else :
341- self ._artifact_validate_result .append_error (
342- message = f"Unexpected format in additional_includes, { additional_include } "
343- )
362+ else :
363+ for result in map (self ._resolve_additional_include_config , additional_includes_configs ):
364+ for local_path , config_info in result :
365+ additional_includes .append (local_path )
366+ self .merge_local_path_to_additional_includes (
367+ local_path = local_path , config_info = config_info , conflict_files = conflict_files
368+ )
344369
345370 # Check the file conflict in local path and artifact package.
346371 conflict_files = {k : v for k , v in conflict_files .items () if len (v ) > 1 }
0 commit comments