@@ -78,22 +78,39 @@ def wrapped(*args, **kwargs):
7878 return dec
7979
8080
81- def parse_json (ext_file_path ):
81+ def parse_json (extension_file_path ):
8282 """Parse a Slicer extension description file.
83- :param ext_file_path : Path to a Slicer extension description file (.json).
83+ :param extension_file_path : Path to a Slicer extension description file (.json).
8484 :return: Dictionary of extension metadata.
8585 """
86- with open (ext_file_path ) as input_file :
86+ with open (extension_file_path ) as input_file :
8787 try :
8888 return json .load (input_file )
8989 except json .JSONDecodeError as exc :
90- extension_name = os .path .splitext (os .path .basename (ext_file_path ))[0 ]
90+ extension_name = os .path .splitext (os .path .basename (extension_file_path ))[0 ]
9191 raise ExtensionParseError (
9292 extension_name ,
9393 textwrap .dedent ("""
9494 Failed to parse '%s': %s
95- """ % (ext_file_path , exc )))
95+ """ % (extension_file_path , exc )))
9696
97+ def check_json_file_format (extension_name , metadata , extension_file_path ):
98+ """Check if the JSON file is properly formatted."""
99+ check_name = "check_json_file_format"
100+ try :
101+ with open (extension_file_path , 'r' , encoding = 'utf-8' ) as f :
102+ json .load (f )
103+ except json .JSONDecodeError as e :
104+ raise ExtensionCheckError (
105+ extension_name , check_name ,
106+ f"Invalid JSON format: { str (e )} " )
107+ # Force using LF-only line endings
108+ with open (extension_file_path , 'r' , encoding = 'utf-8' ) as f :
109+ content = f .read ()
110+ if '\r \n ' in content or '\r ' in content :
111+ raise ExtensionCheckError (
112+ extension_name , check_name ,
113+ "File contains non-LF line endings (CR or CRLF). Please convert to LF-only line endings." )
97114
98115def check_json_schema (extension_name , metadata ):
99116 """Validate extension description JSON against its referenced schema."""
@@ -168,6 +185,20 @@ def check_scm_url_syntax(extension_name, metadata):
168185 extension_name , check_name ,
169186 "scm_url scheme is '%s' but it should by any of %s" % (scheme , supported_schemes ))
170187
188+ def check_extension_name (extension_name , metadata ):
189+ check_name = "check_extension_name"
190+
191+ if extension_name in EXTENSION_NAME_CHECK_EXCEPTIONS :
192+ return
193+
194+ if extension_name .lower ().startswith ("slicer" ):
195+
196+ raise ExtensionCheckError (
197+ extension_name , check_name ,
198+ textwrap .dedent ("""
199+ extension name should not start with 'Slicer'. Please, consider changing it to '%s'.
200+ """ % (
201+ extension_name [7 :],)))
171202
172203@require_metadata_key ("scm_url" )
173204def check_git_repository_name (extension_name , metadata ):
@@ -522,6 +553,8 @@ def main():
522553 extension_description_checks = [
523554 ("Clone repository" , check_clone_repository , {"cloned_repository_folder" : cloned_repository_folder }),
524555 ("Check JSON schema" , check_json_schema , {}),
556+ ("Check JSON file format" , check_json_file_format , {"extension_file_path" : file_path }),
557+ ("Check extension name" , check_extension_name , {}),
525558 ("Check category" , check_category , {}),
526559 ("Check git repository name" , check_git_repository_name , {}),
527560 ("Check SCM URL syntax" , check_scm_url_syntax , {}),
@@ -622,6 +655,66 @@ def main():
622655 "VASSTAlgorithms" ,
623656]
624657
658+ EXTENSION_NAME_CHECK_EXCEPTIONS = [
659+ # These extensions have name with "Slicer" prefix
660+ # (they were created before usage of this prefix was discouraged)
661+ "SlicerAIGT" ,
662+ "SlicerANTs" ,
663+ "SlicerANTsPy" ,
664+ "SlicerAutoscoperM" ,
665+ "SlicerBatchAnonymize" ,
666+ "SlicerBiomech" ,
667+ "SlicerCaseIterator" ,
668+ "SlicerCervicalSpine" ,
669+ "SlicerCineTrack" ,
670+ "SlicerCMF" ,
671+ "SlicerCochlea" ,
672+ "SlicerConda" ,
673+ "SlicerDcm2nii" ,
674+ "SlicerDentalModelSeg" ,
675+ "SlicerDevelopmentToolbox" ,
676+ "SlicerDiffusionComplexityMap" ,
677+ "SlicerDMRI" ,
678+ "SlicerElastix" ,
679+ "SlicerFab" ,
680+ "SlicerFreeSurfer" ,
681+ "SlicerHeadCTDeid" ,
682+ "SlicerHeart" ,
683+ "SlicerIGSIO" ,
684+ "SlicerIGT" ,
685+ "SlicerITKUltrasound" ,
686+ "SlicerJupyter" ,
687+ "SlicerLayoutButtons" ,
688+ "SlicerLiver" ,
689+ "SlicerLookingGlass" ,
690+ "SlicerMarkupConstraints" ,
691+ "SlicerMOOSE" ,
692+ "SlicerMorph" ,
693+ "SlicerMultiverSeg" ,
694+ "SlicerNetstim" ,
695+ "SlicerNeuro" ,
696+ "SlicerNeuropacs" ,
697+ "SlicerNeuroSegmentation" ,
698+ "SlicerOpenAnatomy" ,
699+ "SlicerOpenIGTLink" ,
700+ "SlicerOrbitSurgerySim" ,
701+ "SlicerPRISMRendering" ,
702+ "SlicerProstate" ,
703+ "SlicerProstateAblation" ,
704+ "SlicerPythonTestRunner" ,
705+ "SlicerRadiomics" ,
706+ "SlicerRT" ,
707+ "SlicerSOFA" ,
708+ "SlicerTelemetry" ,
709+ "SlicerThemes" ,
710+ "SlicerToKiwiExporter" ,
711+ "SlicerTractParcellation" ,
712+ "SlicerTrame" ,
713+ "SlicerVirtualMouseCursor" ,
714+ "SlicerVirtualReality" ,
715+ "SlicerVMTK" ,
716+ ]
717+
625718LICENSE_CHECK_EXCEPTIONS = [
626719 "AirwaySegmentation" ,
627720 "AnatomyCarve" ,
0 commit comments