1+ from __future__ import annotations
2+
13import json
2- import sys
34from abc import ABC , abstractmethod
5+ from ast import unparse
46from collections import defaultdict
7+ from collections .abc import Iterable , Iterator
58from enum import Enum
6- from typing import Dict , Iterable , Iterator , List , Optional , Set , Tuple , Type
7-
8- if sys .version_info < (3 , 9 ):
9- from astunparse import unparse
10- else :
11- from ast import unparse
129
1310from fickling .fickle import Interpreter , Pickled , Proto
1411
1512
1613class AnalyzerMeta (type ):
17- _DEFAULT_INSTANCE : Optional [ " Analyzer" ] = None
14+ _DEFAULT_INSTANCE : Analyzer | None = None
1815
1916 @property
20- def default_instance (cls ) -> " Analyzer" :
17+ def default_instance (cls ) -> Analyzer :
2118 if cls ._DEFAULT_INSTANCE is None :
2219 cls ._DEFAULT_INSTANCE = Analyzer (Analysis .ALL )
2320 return cls ._DEFAULT_INSTANCE
@@ -26,11 +23,11 @@ def default_instance(cls) -> "Analyzer":
2623class AnalysisContext :
2724 def __init__ (self , pickled : Pickled ):
2825 self .pickled : Pickled = pickled
29- self .reported_shortened_code : Set [str ] = set ()
30- self .previous_results : List [AnalysisResult ] = []
31- self .results_by_analysis : Dict [ Type [Analysis ], List [AnalysisResult ]] = defaultdict (list )
26+ self .reported_shortened_code : set [str ] = set ()
27+ self .previous_results : list [AnalysisResult ] = []
28+ self .results_by_analysis : dict [ type [Analysis ], list [AnalysisResult ]] = defaultdict (list )
3229
33- def analyze (self , analysis : " Analysis" ) -> "List [AnalysisResult]" :
30+ def analyze (self , analysis : Analysis ) -> list [AnalysisResult ]:
3431 results = list (analysis .analyze (self ))
3532 if not results :
3633 self .results_by_analysis [type (analysis )].append (AnalysisResult (Severity .LIKELY_SAFE ))
@@ -40,10 +37,10 @@ def analyze(self, analysis: "Analysis") -> "List[AnalysisResult]":
4037 return results
4138
4239 @property
43- def results (self ) -> " AnalysisResults" :
40+ def results (self ) -> AnalysisResults :
4441 return AnalysisResults (pickled = self .pickled , results = self .previous_results )
4542
46- def shorten_code (self , ast_node ) -> Tuple [str , bool ]:
43+ def shorten_code (self , ast_node ) -> tuple [str , bool ]:
4744 code = unparse (ast_node ).strip ()
4845 if len (code ) > 32 :
4946 cutoff = code .find ("(" )
@@ -59,10 +56,10 @@ def shorten_code(self, ast_node) -> Tuple[str, bool]:
5956
6057
6158class Analyzer (metaclass = AnalyzerMeta ):
62- def __init__ (self , analyses : Iterable [" Analysis" ]):
63- self .analyses : Tuple [Analysis , ...] = tuple (analyses )
59+ def __init__ (self , analyses : Iterable [Analysis ]):
60+ self .analyses : tuple [Analysis , ...] = tuple (analyses )
6461
65- def analyze (self , pickled : Pickled ) -> " AnalysisResults" :
62+ def analyze (self , pickled : Pickled ) -> AnalysisResults :
6663 context = AnalysisContext (pickled = pickled )
6764 for a in self .analyses :
6865 context .analyze (a )
@@ -101,14 +98,14 @@ class AnalysisResult:
10198 def __init__ (
10299 self ,
103100 severity : Severity ,
104- message : Optional [ str ] = None ,
101+ message : str | None = None ,
105102 analysis_name : str = None ,
106- trigger : Optional [ str ] = None ,
103+ trigger : str | None = None ,
107104 ):
108105 self .severity : Severity = severity
109- self .message : Optional [ str ] = message
106+ self .message : str | None = message
110107 self .analysis_name : str = analysis_name
111- self .trigger : Optional [ str ] = trigger # Field to store the trigger code fragment or artifact
108+ self .trigger : str | None = trigger # Field to store the trigger code fragment or artifact
112109
113110 def __lt__ (self , other ):
114111 return isinstance (other , AnalysisResult ) and (
@@ -127,7 +124,7 @@ def __str__(self):
127124
128125
129126class Analysis (ABC ):
130- ALL : "List [Analysis]" = []
127+ ALL : list [Analysis ] = []
131128
132129 def __init_subclass__ (cls , ** kwargs ):
133130 Analysis .ALL .append (cls ())
@@ -140,7 +137,7 @@ def analyze(self, context: AnalysisContext) -> Iterator[AnalysisResult]:
140137class DuplicateProtoAnalysis (Analysis ):
141138 def analyze (self , context : AnalysisContext ) -> Iterator [AnalysisResult ]:
142139 had_proto = False
143- proto_versions : Set [int ] = set ()
140+ proto_versions : set [int ] = set ()
144141 for i , opcode in enumerate (context .pickled ):
145142 if isinstance (opcode , Proto ):
146143 if had_proto :
@@ -344,7 +341,7 @@ def analyze(self, context: AnalysisContext) -> Iterator[AnalysisResult]:
344341class AnalysisResults :
345342 def __init__ (self , pickled : Pickled , results : Iterable [AnalysisResult ]):
346343 self .pickled : Pickled = pickled
347- self .results : Tuple [AnalysisResult , ...] = tuple (results )
344+ self .results : tuple [AnalysisResult , ...] = tuple (results )
348345
349346 @property
350347 def severity (self ) -> Severity :
@@ -356,7 +353,7 @@ def __bool__(self):
356353 """Returns True if all analyses failed to find any unsafe operations"""
357354 return all (map (bool , sorted (self .results )))
358355
359- def detailed_results (self ) -> Dict [str , Dict [str , str ]]:
356+ def detailed_results (self ) -> dict [str , dict [str , str ]]:
360357 detailed = defaultdict (dict )
361358 for result in self .results :
362359 if result .trigger :
@@ -386,9 +383,9 @@ def to_dict(self, verbosity: Severity = Severity.POSSIBLY_UNSAFE):
386383
387384def check_safety (
388385 pickled : Pickled ,
389- analyzer : Optional [ Analyzer ] = None ,
386+ analyzer : Analyzer | None = None ,
390387 verbosity : Severity = Severity .POSSIBLY_UNSAFE ,
391- json_output_path : Optional [ str ] = None ,
388+ json_output_path : str | None = None ,
392389) -> AnalysisResults :
393390 if analyzer is None :
394391 analyzer = Analyzer .default_instance
0 commit comments