1- import os
2- import io
3- import json
4- import hashlib
5- import glob
61import datetime
2+ import hashlib
3+ import json
4+ import os
5+ import pathlib
76from enum import IntFlag
7+ from typing import Any , Dict , List , Union
8+
89from logzero import logger as log
910
10- PLATFORMS = ['win32' , 'linux' , 'linux-deb' , 'linux-rpm' ,
11- 'darwin' , 'linux-snap' , 'server-linux' ]
12- ARCHITECTURES = ['' , 'x64' ]
13- BUILDTYPES = ['' , 'archive' , 'user' ]
14- QUALITIES = ['stable' , 'insider' ]
11+ PLATFORMS = ["win32" , "linux" , "linux-deb" , "linux-rpm" , "darwin" , "linux-snap" , "server-linux" ]
12+ ARCHITECTURES = ["" , "x64" ]
13+ BUILDTYPES = ["" , "archive" , "user" ]
14+ QUALITIES = ["stable" , "insider" ]
1515
16- URL_BINUPDATES = r' https://update.code.visualstudio.com/api/update/'
17- URL_RECOMMENDATIONS = r' https://az764295.vo.msecnd.net/extensions/workspaceRecommendations.json.gz'
18- URL_MARKETPLACEQUERY = r' https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery'
19- URL_MALICIOUS = r' https://az764295.vo.msecnd.net/extensions/marketplace.json'
16+ URL_BINUPDATES = r" https://update.code.visualstudio.com/api/update/"
17+ URL_RECOMMENDATIONS = r" https://az764295.vo.msecnd.net/extensions/workspaceRecommendations.json.gz"
18+ URL_MARKETPLACEQUERY = r" https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery"
19+ URL_MALICIOUS = r" https://az764295.vo.msecnd.net/extensions/marketplace.json"
2020
21- URLROOT = ' https://update.code.visualstudio.com'
22- ARTIFACTS = ' /artifacts/'
23- ARTIFACTS_INSTALLERS = ' /artifacts/installers'
24- ARTIFACTS_EXTENSIONS = ' /artifacts/extensions'
25- ARTIFACT_RECOMMENDATION = ' /artifacts/recommendations.json'
26- ARTIFACT_MALICIOUS = ' /artifacts/malicious.json'
21+ URLROOT = " https://update.code.visualstudio.com"
22+ ARTIFACTS = " /artifacts/"
23+ ARTIFACTS_INSTALLERS = " /artifacts/installers"
24+ ARTIFACTS_EXTENSIONS = " /artifacts/extensions"
25+ ARTIFACT_RECOMMENDATION = " /artifacts/recommendations.json"
26+ ARTIFACT_MALICIOUS = " /artifacts/malicious.json"
2727
2828TIMEOUT = 12
2929
3030
3131class QueryFlags (IntFlag ):
32- __no_flags_name__ = ' NoneDefined'
32+ __no_flags_name__ = " NoneDefined"
3333 NoneDefined = 0x0
3434 IncludeVersions = 0x1
3535 IncludeFiles = 0x2
@@ -45,7 +45,7 @@ class QueryFlags(IntFlag):
4545
4646
4747class FilterType (IntFlag ):
48- __no_flags_name__ = ' Target'
48+ __no_flags_name__ = " Target"
4949 Tag = 1
5050 ExtensionId = 4
5151 Category = 5
@@ -58,7 +58,7 @@ class FilterType(IntFlag):
5858
5959
6060class SortBy (IntFlag ):
61- __no_flags_name__ = ' NoneOrRelevance'
61+ __no_flags_name__ = " NoneOrRelevance"
6262 NoneOrRelevance = 0
6363 LastUpdatedDate = 1
6464 Title = 2
@@ -70,109 +70,118 @@ class SortBy(IntFlag):
7070
7171
7272class SortOrder (IntFlag ):
73- __no_flags_name__ = ' Default'
73+ __no_flags_name__ = " Default"
7474 Default = 0
7575 Ascending = 1
7676 Descending = 2
7777
7878
7979class MagicJsonEncoder (json .JSONEncoder ):
80- def default (self , o ):
81- if isinstance (o , datetime .datetime ):
82- return o .isoformat ()
83- return o .__dict__
80+ def default (self , o : Any ) -> Union [str , Dict [str , Any ]]:
81+ try :
82+ return super ().default (o )
83+ except TypeError as err :
84+ # could be datetime
85+ if isinstance (o , datetime .datetime ):
86+ return o .isoformat ()
87+ # could also be cls with slots
88+ try :
89+ return {key : getattr (o , key , None ) for key in o .__slots__ }
90+ except AttributeError :
91+ pass
92+ # finally, should have a dict if it is a dataclass or another cls
93+ try :
94+ return o .__dict__
95+ except AttributeError :
96+ raise TypeError (
97+ "Can't encode object. Tried isoformat of datetime, class slots and class dict"
98+ ) from err
8499
85100
86- class Utility ( object ) :
101+ class Utility :
87102 """
88103 Utility tool
89104 """
90105
91106 @staticmethod
92- def hash_file_and_check (filepath , expectedchecksum ) :
107+ def hash_file_and_check (filepath : Union [ str , pathlib . Path ], expectedchecksum : str ) -> bool :
93108 """
94- Hashes a file and checks for the expected checksum
109+ Hashes a file and checks for the expected checksum.
110+ Checksum is sha256 default implementation.
95111 """
96112 h = hashlib .sha256 ()
97- with open (filepath , 'rb' ) as f :
98- for chunk in iter (lambda : f .read (4096 ), b'' ):
113+ with open (filepath , "rb" ) as f :
114+ for chunk in iter (lambda : f .read (4096 ), b"" ):
99115 h .update (chunk )
100- if expectedchecksum != h .hexdigest ():
101- return False
102-
103- return True
116+ return expectedchecksum == h .hexdigest ()
104117
105118 @staticmethod
106- def load_json (filepath ):
119+ def load_json (filepath : Union [str , pathlib .Path ]) -> Union [List [Any ], Dict [str , Any ]]:
120+ if isinstance (filepath , str ):
121+ filepath : pathlib .Path = pathlib .Path (filepath )
122+
107123 result = []
108- if not os .path .exists (filepath ):
109- log .debug (f'Unable to load json from { filepath } ' )
110- return []
111- with io .open (filepath , 'r' , encoding = 'utf-8-sig' ) as fp :
124+ if not filepath .exists ():
125+ log .debug (f"Unable to load json from { filepath .absolute ()} . Does not exist." )
126+ return result
127+ elif filepath .is_dir ():
128+ log .debug (f"Cannot load json at path { filepath .absolute ()} . It is a directory" )
129+ return result
130+
131+ with open (filepath , "r" , encoding = "utf-8-sig" ) as fp :
112132 try :
113133 result = json .load (fp )
114134 if not result :
115135 return []
116- except json .decoder .JSONDecodeError :
117- log .debug (f' JSONDecodeError while processing { filepath } ' )
136+ except json .decoder .JSONDecodeError as err :
137+ log .debug (f" JSONDecodeError while processing { filepath . absolute () } \n error: { str ( err ) } " )
118138 return []
119139 return result
120140
121141 @staticmethod
122- def write_json (filepath , content ) :
123- with open (filepath , 'w' ) as outfile :
142+ def write_json (filepath : Union [ str , pathlib . Path ], content : Dict [ str , Any ]) -> None :
143+ with open (filepath , "w" ) as outfile :
124144 json .dump (content , outfile , cls = MagicJsonEncoder , indent = 4 )
125145
126146 @staticmethod
127- def first_file (filepath , reverse = False ):
128- results = glob .glob (filepath )
129- if reverse :
147+ def first_file (filepath : Union [str , pathlib .Path ], pattern : str , reverse : bool = False ) -> Union [str , bool ]:
148+ if isinstance (filepath , str ):
149+ filepath = pathlib .Path (filepath )
150+ results = [* filepath .glob (pattern )]
151+ if not results :
152+ return False
153+ elif len (results ) >= 1 and reverse :
130154 results .sort (reverse = True )
131- # log.info(filepath)
132- if results and len (results ) >= 1 :
133- return results [0 ]
134- return False
155+ return str (results [0 ].absolute ())
135156
136157 @staticmethod
137- def folders_in_folder (filepath ) :
158+ def folders_in_folder (filepath : str ) -> List [ str ] :
138159 return [f for f in os .listdir (filepath ) if os .path .isdir (os .path .join (filepath , f ))]
139160
140161 @staticmethod
141- def files_in_folder (filepath ) :
162+ def files_in_folder (filepath : str ) -> List [ str ] :
142163 return [f for f in os .listdir (filepath ) if os .path .isfile (os .path .join (filepath , f ))]
143164
144165 @staticmethod
145- def seconds_to_human_time (seconds ) :
166+ def seconds_to_human_time (seconds : int ) -> str :
146167 return str (datetime .timedelta (seconds = seconds ))
147168
148169 @staticmethod
149- def from_json_datetime (jsondate ) :
150- datetime .datetime .strptime (jsondate , ' %Y-%m-%dT%H:%M:%S.%fZ' )
170+ def from_json_datetime (jsondate : str ) -> datetime . datetime :
171+ return datetime .datetime .strptime (jsondate , " %Y-%m-%dT%H:%M:%S.%fZ" )
151172
152173 @staticmethod
153- def validate_platform (platform ):
154- if platform in PLATFORMS :
155- return True
156- else :
157- return False
174+ def validate_platform (platform : str ) -> bool :
175+ return platform in PLATFORMS
158176
159177 @staticmethod
160- def validate_architecture (arch ):
161- if arch in ARCHITECTURES :
162- return True
163- else :
164- return False
178+ def validate_architecture (arch : str ) -> bool :
179+ return arch in ARCHITECTURES
165180
166181 @staticmethod
167- def validate_buildtype (buildtype ):
168- if buildtype in BUILDTYPES :
169- return True
170- else :
171- return False
182+ def validate_buildtype (buildtype : str ) -> bool :
183+ return buildtype in BUILDTYPES
172184
173185 @staticmethod
174- def validate_quality (quality ):
175- if quality in QUALITIES :
176- return True
177- else :
178- return False
186+ def validate_quality (quality : str ) -> bool :
187+ return quality in QUALITIES
0 commit comments