55
66from collections import namedtuple
77import csv
8- import importlib .util
98import json
109import os
1110import platform
1211import re
1312import subprocess
1413import sys
1514import time
16- from typing import Tuple
1715
1816
1917Version = namedtuple ("Version" , ["version" , "constraint" ])
2018
2119
22- def parse_version (version : str ) -> Tuple :
23- constraint_idx = re .search (r"\d" , version ).start ()
24- numeric = version [constraint_idx :]
25- constraint = version [:constraint_idx ]
26- parsed_version = tuple (int (re .sub ("[^0-9]" , "" , p )) for p in numeric .split ("." ))
27- return Version (parsed_version , constraint )
20+ def parse_version (version ):
21+ try :
22+ constraint_match = re .search (r"\d" , version )
23+ if not constraint_match :
24+ return Version ((0 , 0 ), "" )
25+ constraint_idx = constraint_match .start ()
26+ numeric = version [constraint_idx :]
27+ constraint = version [:constraint_idx ]
28+ parsed_version = tuple (int (re .sub ("[^0-9]" , "" , p )) for p in numeric .split ("." ))
29+ return Version (parsed_version , constraint )
30+ except Exception :
31+ return Version ((0 , 0 ), "" )
2832
2933
3034SCRIPT_DIR = os .path .dirname (__file__ )
3135RUNTIMES_ALLOW_LIST = {
32- "cpython" : {"min" : parse_version ("3.7" ), "max" : parse_version ("3.13" )},
36+ "cpython" : {
37+ "min" : Version (version = (3 , 7 ), constraint = "" ),
38+ "max" : Version (version = (3 , 13 ), constraint = "" ),
39+ }
3340}
3441
3542FORCE_INJECT = os .environ .get ("DD_INJECT_FORCE" , "" ).lower () in ("true" , "1" , "t" )
3643FORWARDER_EXECUTABLE = os .environ .get ("DD_TELEMETRY_FORWARDER_PATH" , "" )
3744TELEMETRY_ENABLED = "DD_INJECTION_ENABLED" in os .environ
3845DEBUG_MODE = os .environ .get ("DD_TRACE_DEBUG" , "" ).lower () in ("true" , "1" , "t" )
39- INSTALLED_PACKAGES = None
40- PYTHON_VERSION = None
41- PYTHON_RUNTIME = None
42- PKGS_ALLOW_LIST = None
43- EXECUTABLES_DENY_LIST = None
46+ INSTALLED_PACKAGES = {}
47+ PYTHON_VERSION = "unknown"
48+ PYTHON_RUNTIME = "unknown"
49+ PKGS_ALLOW_LIST = {}
50+ EXECUTABLES_DENY_LIST = set ()
4451VERSION_COMPAT_FILE_LOCATIONS = (
4552 os .path .abspath (os .path .join (SCRIPT_DIR , "../datadog-lib/min_compatible_versions.csv" )),
4653 os .path .abspath (os .path .join (SCRIPT_DIR , "min_compatible_versions.csv" )),
@@ -103,7 +110,7 @@ def build_denied_executables():
103110 cleaned = line .strip ("\n " )
104111 denied_executables .add (cleaned )
105112 denied_executables .add (os .path .basename (cleaned ))
106- _log (f "Built denied-executables list of { len (denied_executables )} entries" , level = "debug" )
113+ _log ("Built denied-executables list of %s entries" % ( len (denied_executables ),) , level = "debug" )
107114 return denied_executables
108115
109116
@@ -143,9 +150,15 @@ def send_telemetry(event):
143150 stderr = subprocess .PIPE ,
144151 universal_newlines = True ,
145152 )
146- p .stdin .write (event_json )
147- p .stdin .close ()
148- _log ("wrote telemetry to %s" % FORWARDER_EXECUTABLE , level = "debug" )
153+ if p .stdin :
154+ p .stdin .write (event_json )
155+ p .stdin .close ()
156+ _log ("wrote telemetry to %s" % FORWARDER_EXECUTABLE , level = "debug" )
157+ else :
158+ _log (
159+ "failed to write telemetry to %s, could not write to telemetry writer stdin" % FORWARDER_EXECUTABLE ,
160+ level = "error" ,
161+ )
149162
150163
151164def _get_clib ():
@@ -154,7 +167,7 @@ def _get_clib():
154167 If GNU is not detected then returns MUSL.
155168 """
156169
157- libc , version = platform .libc_ver ()
170+ libc , _ = platform .libc_ver ()
158171 if libc == "glibc" :
159172 return "gnu"
160173 return "musl"
@@ -170,7 +183,9 @@ def _log(msg, *args, **kwargs):
170183 if DEBUG_MODE :
171184 asctime = time .strftime ("%Y-%m-%d %H:%M:%S" , time .localtime ())
172185 msg = "[%s] [%s] datadog.autoinstrumentation(pid: %d): " % (asctime , level .upper (), os .getpid ()) + msg % args
173- print (msg , file = sys .stderr )
186+ sys .stderr .write (msg )
187+ sys .stderr .write ("\n " )
188+ sys .stderr .flush ()
174189
175190
176191def runtime_version_is_supported (python_runtime , python_version ):
@@ -197,13 +212,13 @@ def get_first_incompatible_sysarg():
197212 _log ("sys.argv not available, skipping sys.argv check" , level = "debug" )
198213 return
199214
200- _log (f "Checking sysargs : len(argv): { len (sys .argv )} " , level = "debug" )
215+ _log ("Checking sys.args : len(sys. argv): %s" % ( len (sys .argv ),) , level = "debug" )
201216 if len (sys .argv ) <= 1 :
202217 return
203218 argument = sys .argv [0 ]
204- _log (f "Is argument { argument } in deny-list?" , level = "debug" )
219+ _log ("Is argument %s in deny-list?" % ( argument ,) , level = "debug" )
205220 if argument in EXECUTABLES_DENY_LIST or os .path .basename (argument ) in EXECUTABLES_DENY_LIST :
206- _log (f "argument { argument } is in deny-list" , level = "debug" )
221+ _log ("argument %s is in deny-list" % ( argument ,) , level = "debug" )
207222 return argument
208223
209224
@@ -225,7 +240,13 @@ def _inject():
225240 os .environ ["_DD_INJECT_WAS_ATTEMPTED" ] = "true"
226241 spec = None
227242 try :
243+ # `find_spec` is only available in Python 3.4+
244+ # https://docs.python.org/3/library/importlib.html#importlib.util.find_spec
245+ # DEV: It is ok to fail here on import since it'll only fail on Python versions we don't support / inject into
246+ import importlib .util
247+
228248 # None is a valid return value for find_spec (module was not found), so we need to check for it explicitly
249+
229250 spec = importlib .util .find_spec ("ddtrace" )
230251 if not spec :
231252 raise ModuleNotFoundError ("ddtrace" )
@@ -377,5 +398,11 @@ def _inject():
377398
378399try :
379400 _inject ()
380- except Exception :
381- pass # absolutely never allow exceptions to propagate to the app
401+ except Exception as e :
402+ try :
403+ event = gen_telemetry_payload (
404+ [create_count_metric ("library_entrypoint.error" , ["error_type:" + type (e ).__name__ .lower ()])]
405+ )
406+ send_telemetry (event )
407+ except Exception :
408+ pass # absolutely never allow exceptions to propagate to the app
0 commit comments