11import ctypes
22import glob
3+ import json
34import os
45import platform
56import re
67import sys
8+ import urllib .error
9+ import urllib .request
710from dataclasses import dataclass , field , fields
811from pathlib import Path
912from platform import machine
10- from typing import List , Optional
11-
12- import requests
13- from tqdm import tqdm
13+ from typing import List , Optional , Tuple
1414
1515__all__ = ["TLSLibrary" ]
1616
17+ LATEST_VERSION_TAG_NAME = "v1.11.2"
1718BIN_DIR = os .path .join (Path (__file__ ).resolve (strict = True ).parent .parent / "bin" )
1819GITHUB_API_URL = "https://api.github.com/repos/bogdanfinn/tls-client/releases"
1920PLATFORM = sys .platform
@@ -73,7 +74,7 @@ def model_fields_set(cls) -> set:
7374 @classmethod
7475 def from_kwargs (cls , ** kwargs ):
7576 model_fields_set = cls .model_fields_set ()
76- return cls (** {k : v for k , v in kwargs .items () if k in model_fields_set })
77+ return cls (** {k : v for k , v in kwargs .items () if k in model_fields_set }) # noqa
7778
7879
7980@dataclass
@@ -133,92 +134,64 @@ class TLSLibrary:
133134
134135 _PATH : str = None
135136 _STATIC_API_DATA = {
136- "name" : "v1.7.10 " ,
137- "tag_name" : "v1.7.10 " ,
137+ "name" : "v1.11.2 " ,
138+ "tag_name" : "v1.11.2 " ,
138139 "assets" : [
139- {
140- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-darwin-amd64-1.7.10.dylib" ,
141- "name" : "tls-client-darwin-amd64-1.7.10.dylib" ,
142- },
143- {
144- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-darwin-arm64-1.7.10.dylib" ,
145- "name" : "tls-client-darwin-arm64-1.7.10.dylib" ,
146- },
147- {
148- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-linux-arm64-1.7.10.so" ,
149- "name" : "tls-client-linux-arm64-1.7.10.so" ,
150- },
151- {
152- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-linux-armv7-1.7.10.so" ,
153- "name" : "tls-client-linux-armv7-1.7.10.so" ,
154- },
155- {
156- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-linux-ubuntu-amd64-1.7.10.so" ,
157- "name" : "tls-client-linux-ubuntu-amd64-1.7.10.so" ,
158- },
159- {
160- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-windows-32-1.7.10.dll" ,
161- "name" : "tls-client-windows-32-1.7.10.dll" ,
162- },
163- {
164- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-windows-64-1.7.10.dll" ,
165- "name" : "tls-client-windows-64-1.7.10.dll" ,
166- },
167- {
168- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-darwin-amd64.dylib" ,
169- "name" : "tls-client-xgo-1.7.10-darwin-amd64.dylib" ,
170- },
171- {
172- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-darwin-arm64.dylib" ,
173- "name" : "tls-client-xgo-1.7.10-darwin-arm64.dylib" ,
174- },
175- {
176- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-386.so" ,
177- "name" : "tls-client-xgo-1.7.10-linux-386.so" ,
178- },
179- {
180- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-amd64.so" ,
181- "name" : "tls-client-xgo-1.7.10-linux-amd64.so" ,
182- },
183- {
184- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-arm-5.so" ,
185- "name" : "tls-client-xgo-1.7.10-linux-arm-5.so" ,
186- },
187- {
188- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-arm-6.so" ,
189- "name" : "tls-client-xgo-1.7.10-linux-arm-6.so" ,
190- },
191- {
192- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-arm-7.so" ,
193- "name" : "tls-client-xgo-1.7.10-linux-arm-7.so" ,
194- },
195- {
196- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-arm64.so" ,
197- "name" : "tls-client-xgo-1.7.10-linux-arm64.so" ,
198- },
199- {
200- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-ppc64le.so" ,
201- "name" : "tls-client-xgo-1.7.10-linux-ppc64le.so" ,
202- },
203- {
204- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-riscv64.so" ,
205- "name" : "tls-client-xgo-1.7.10-linux-riscv64.so" ,
206- },
207- {
208- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-linux-s390x.so" ,
209- "name" : "tls-client-xgo-1.7.10-linux-s390x.so" ,
210- },
211- {
212- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-windows-386.dll" ,
213- "name" : "tls-client-xgo-1.7.10-windows-386.dll" ,
214- },
215- {
216- "browser_download_url" : "https://github.com/bogdanfinn/tls-client/releases/download/v1.7.10/tls-client-xgo-1.7.10-windows-amd64.dll" ,
217- "name" : "tls-client-xgo-1.7.10-windows-amd64.dll" ,
218- },
219- ],
140+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-darwin-amd64-1.11.2.dylib' , 'name' : 'tls-client-darwin-amd64-1.11.2.dylib' },
141+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-darwin-arm64-1.11.2.dylib' , 'name' : 'tls-client-darwin-arm64-1.11.2.dylib' },
142+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-linux-alpine-amd64-1.11.2.so' , 'name' : 'tls-client-linux-alpine-amd64-1.11.2.so' },
143+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-linux-arm64-1.11.2.so' , 'name' : 'tls-client-linux-arm64-1.11.2.so' },
144+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-linux-armv7-1.11.2.so' , 'name' : 'tls-client-linux-armv7-1.11.2.so' },
145+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-linux-ubuntu-amd64-1.11.2.so' , 'name' : 'tls-client-linux-ubuntu-amd64-1.11.2.so' },
146+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-windows-32-1.11.2.dll' , 'name' : 'tls-client-windows-32-1.11.2.dll' },
147+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-windows-64-1.11.2.dll' , 'name' : 'tls-client-windows-64-1.11.2.dll' },
148+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-darwin-amd64.dylib' , 'name' : 'tls-client-xgo-1.11.2-darwin-amd64.dylib' },
149+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-darwin-arm64.dylib' , 'name' : 'tls-client-xgo-1.11.2-darwin-arm64.dylib' },
150+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-386.so' , 'name' : 'tls-client-xgo-1.11.2-linux-386.so' },
151+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-amd64.so' , 'name' : 'tls-client-xgo-1.11.2-linux-amd64.so' },
152+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-arm-5.so' , 'name' : 'tls-client-xgo-1.11.2-linux-arm-5.so' },
153+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-arm-6.so' , 'name' : 'tls-client-xgo-1.11.2-linux-arm-6.so' },
154+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-arm-7.so' , 'name' : 'tls-client-xgo-1.11.2-linux-arm-7.so' },
155+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-arm64.so' , 'name' : 'tls-client-xgo-1.11.2-linux-arm64.so' },
156+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-ppc64le.so' , 'name' : 'tls-client-xgo-1.11.2-linux-ppc64le.so' },
157+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-riscv64.so' , 'name' : 'tls-client-xgo-1.11.2-linux-riscv64.so' },
158+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-linux-s390x.so' , 'name' : 'tls-client-xgo-1.11.2-linux-s390x.so' },
159+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-windows-386.dll' , 'name' : 'tls-client-xgo-1.11.2-windows-386.dll' },
160+ {'browser_download_url' : 'https://github.com/bogdanfinn/tls-client/releases/download/v1.11.2/tls-client-xgo-1.11.2-windows-amd64.dll' , 'name' : 'tls-client-xgo-1.11.2-windows-amd64.dll' }],
220161 }
221162
163+ @staticmethod
164+ def _parse_version (version_string : str ) -> Tuple [int , ...]:
165+ """Converts a version string (e.g., "v1.11.2") to a comparable tuple (1, 11, 2)."""
166+ try :
167+ parts = version_string .lstrip ('v' ).split ('.' )
168+ return tuple (map (int , parts ))
169+ except (ValueError , AttributeError ):
170+ return 0 , 0 , 0
171+
172+ @staticmethod
173+ def _parse_version_from_filename (filename : str ) -> Tuple [int , ...]:
174+ """Extracts and parses the version from a library filename."""
175+ match = re .search (r'v?(\d+\.\d+\.\d+)' , Path (filename ).name )
176+ if match :
177+ return TLSLibrary ._parse_version (match .group (1 ))
178+ return 0 , 0 , 0
179+
180+ @classmethod
181+ def cleanup_files (cls , keep_file : str = None ):
182+ """Removes all library files in the BIN_DIR except for the one to keep."""
183+ for file_path in cls .find_all ():
184+ is_remove = True
185+ if keep_file and Path (file_path ).name == Path (keep_file ).name :
186+ is_remove = False
187+
188+ if is_remove :
189+ try :
190+ os .remove (file_path )
191+ print (f"Removed old library file: { file_path } " )
192+ except OSError as e :
193+ print (f"Error removing old library file { file_path } : { e } " )
194+
222195 @classmethod
223196 def fetch_api (cls , version : str = None , retries : int = 3 ):
224197 def _find_release (data , version_ : str = None ):
@@ -250,11 +223,12 @@ def _find_release(data, version_: str = None):
250223 asset_urls , ubuntu_urls = [], []
251224 for _ in range (retries ):
252225 try :
253- response = requests .get (GITHUB_API_URL )
254- if response .ok :
255- _find_release (response .json ())
256- break
257-
226+ # Use standard library's urllib to fetch API data
227+ with urllib .request .urlopen (GITHUB_API_URL , timeout = 10 ) as response :
228+ if response .status == 200 :
229+ content = response .read ().decode ('utf-8' )
230+ _find_release (json .loads (content ))
231+ break
258232 except Exception as ex :
259233 print ("Unable to fetch GitHub API: %s" % ex )
260234
@@ -272,6 +246,7 @@ def find(cls) -> str:
272246 for fp in cls .find_all ():
273247 if PATTERN_RE .search (fp ):
274248 return fp
249+ return None
275250
276251 @classmethod
277252 def find_all (cls ) -> List [str ]:
@@ -293,80 +268,111 @@ def download(cls, version: str = None) -> str:
293268 )
294269 )
295270 download_url = None
296- for download_url in cls .fetch_api (version ):
297- if download_url :
271+ for url in cls .fetch_api (version ):
272+ if url :
273+ download_url = url
298274 break
299275
300276 print ("Library Download URL: %s" % download_url )
301277 if download_url :
302- destination = os .path .join (BIN_DIR , download_url .split ("/" )[- 1 ])
303- with requests .get (download_url , stream = True ) as response :
304- response .raise_for_status ()
278+ destination_name = download_url .split ("/" )[- 1 ]
279+ destination = os .path .join (BIN_DIR , destination_name )
280+
281+ # Use standard library's urllib to download the file
282+ with urllib .request .urlopen (download_url , timeout = 10 ) as response :
283+ if response .status != 200 :
284+ raise urllib .error .URLError (f"Failed to download file: HTTP { response .status } " )
285+
305286 os .makedirs (BIN_DIR , exist_ok = True )
306287 total_size = int (response .headers .get ("content-length" , 0 ))
307- chunk_size = 1024
308- with open (
309- os .path .join (BIN_DIR , download_url .split ("/" )[- 1 ]), "wb"
310- ) as file , tqdm (
311- desc = destination ,
312- total = total_size ,
313- unit = "iB" ,
314- unit_scale = True ,
315- unit_divisor = chunk_size ,
316- ) as progress_bar :
317- for chunk in response .iter_content (chunk_size ):
318- size = file .write (chunk )
319- progress_bar .update (size )
320-
288+ chunk_size = 8192 # 8KB
289+
290+ with open (destination , "wb" ) as file :
291+ downloaded = 0
292+ while True :
293+ chunk = response .read (chunk_size )
294+ if not chunk :
295+ break
296+
297+ file .write (chunk )
298+ downloaded += len (chunk )
299+
300+ # Simple text-based progress bar
301+ if total_size > 0 :
302+ percent = downloaded / total_size * 100
303+ bar_length = 50
304+ filled_length = int (bar_length * downloaded // total_size )
305+ bar = "=" * filled_length + '-' * (bar_length - filled_length )
306+ sys .stdout .write (f'\r Downloading { destination_name } : [{ bar } ] { percent :.1f} %' )
307+ sys .stdout .flush ()
308+
309+ print () # Newline after download completes
321310 return destination
322311
323- except requests . exceptions . HTTPError as ex :
312+ except ( urllib . error . URLError , urllib . error . HTTPError ) as ex :
324313 print ("Unable to download file: %s" % ex )
314+ except Exception as e :
315+ print ("An unexpected error occurred during download: %s" % e )
325316
326317 @classmethod
327318 def set_path (cls , fp : str ):
328319 cls ._PATH = fp
329320
330321 @classmethod
331322 def load (cls ):
332- """Load libraries"""
323+ """
324+ Loads the TLS library. It checks for the correct version, downloads it if
325+ the local version is outdated or missing, and then loads it into memory.
326+ """
333327
334- def _load_libraries (fp_ ):
328+ def _load_library (fp_ ):
335329 try :
336330 lib = ctypes .cdll .LoadLibrary (fp_ )
337331 cls .set_path (fp_ )
332+ print (f"Successfully loaded TLS library: { fp_ } " )
338333 return lib
339334 except Exception as ex :
340- print ("Unable to load TLS Library , details: %s" % ex )
335+ print (f "Unable to load TLS library ' { fp_ } ' , details: { ex } " )
341336 try :
342337 os .remove (fp_ )
343- except FileNotFoundError :
338+ except ( FileNotFoundError , PermissionError ) :
344339 pass
345340
346- if cls ._PATH is not None :
347- library = _load_libraries (cls ._PATH )
348- if library :
349- return library
350-
351- if TLS_LIBRARY_PATH :
352- library = _load_libraries (TLS_LIBRARY_PATH )
353- if library :
354- return library
355-
356- for fp in cls .find_all ():
357- if IS_UBUNTU and PATTERN_UBUNTU_RE .search (fp ):
358- library = _load_libraries (fp )
359- if library :
360- return library
361- if PATTERN_RE .search (fp ):
362- library = _load_libraries (fp )
341+ target_version = cls ._parse_version (LATEST_VERSION_TAG_NAME )
342+ print (f"Required library version: { LATEST_VERSION_TAG_NAME } " )
343+ local_files = cls .find_all ()
344+ newest_local_version = (0 , 0 , 0 )
345+ newest_local_file = None
346+
347+ if local_files :
348+ for file_path in local_files :
349+ file_version = cls ._parse_version_from_filename (file_path )
350+ if file_version > newest_local_version :
351+ newest_local_version = file_version
352+ newest_local_file = file_path
353+ print (
354+ f"Found newest local library: { newest_local_file } (version { '.' .join (map (str , newest_local_version ))} )" )
355+ else :
356+ print ("No local library found." )
357+
358+ if newest_local_version < target_version :
359+ if newest_local_file :
360+ print (f"Local library is outdated. Upgrading to { LATEST_VERSION_TAG_NAME } ..." )
361+ else :
362+ print (f"Downloading required library version { LATEST_VERSION_TAG_NAME } ..." )
363+
364+ downloaded_fp = cls .download (version = LATEST_VERSION_TAG_NAME )
365+ if downloaded_fp :
366+ cls .cleanup_files (keep_file = downloaded_fp )
367+ library = _load_library (downloaded_fp )
363368 if library :
364369 return library
370+ raise OSError ("Failed to download the required TLS library." )
365371
366- download_fp = cls .download ()
367- if download_fp :
368- library = _load_libraries (download_fp )
372+ if newest_local_file :
373+ library = _load_library (newest_local_file )
369374 if library :
375+ cls .cleanup_files (keep_file = newest_local_file )
370376 return library
371377
372- raise OSError ("Your system does not support TLS Library ." )
378+ raise OSError ("Could not find or load a compatible TLS library ." )
0 commit comments