6363import typing
6464from json import dumps
6565from os import environ
66+ from types import ModuleType
67+ from typing import Dict , List , Optional , Union , cast
6668from urllib import parse
6769
6870from opentelemetry .attributes import BoundedAttributes
7577from opentelemetry .util ._importlib_metadata import entry_points , version
7678from opentelemetry .util .types import AttributeValue
7779
80+ psutil : Optional [ModuleType ] = None
81+
7882try :
79- import psutil
83+ import psutil as pustil_module
84+
85+ pustil = pustil_module
8086except ImportError :
81- psutil = None
87+ pass
8288
8389LabelValue = AttributeValue
8490Attributes = typing .Mapping [str , LabelValue ]
147153class Resource :
148154 """A Resource is an immutable representation of the entity producing telemetry as Attributes."""
149155
156+ _attributes : Dict [
157+ str , Union [str , int , float , bool ]
158+ ] # Example: Adjust according to actual expected types
159+ _schema_url : str
160+
150161 def __init__ (
151162 self , attributes : Attributes , schema_url : typing .Optional [str ] = None
152163 ):
@@ -173,7 +184,7 @@ def create(
173184 if not attributes :
174185 attributes = {}
175186
176- resource_detectors = []
187+ resource_detectors : List [ ResourceDetector ] = []
177188
178189 resource = _DEFAULT_RESOURCE
179190
@@ -182,6 +193,7 @@ def create(
182193 ).split ("," )
183194
184195 if "otel" not in otel_experimental_resource_detectors :
196+
185197 otel_experimental_resource_detectors .append ("otel" )
186198
187199 for resource_detector in otel_experimental_resource_detectors :
@@ -193,9 +205,8 @@ def create(
193205 name = resource_detector .strip (),
194206 )
195207 )
196- ). load ()()
208+ )
197209 )
198-
199210 resource = get_aggregated_resources (
200211 resource_detectors , _DEFAULT_RESOURCE
201212 ).merge (Resource (attributes , schema_url ))
@@ -206,7 +217,7 @@ def create(
206217 PROCESS_EXECUTABLE_NAME , None
207218 )
208219 if process_executable_name :
209- default_service_name += ":" + process_executable_name
220+ default_service_name += ":" + str ( process_executable_name )
210221 resource = resource .merge (
211222 Resource ({SERVICE_NAME : default_service_name }, schema_url )
212223 )
@@ -218,6 +229,8 @@ def get_empty() -> "Resource":
218229
219230 @property
220231 def attributes (self ) -> Attributes :
232+ if self ._attributes is None :
233+ raise ValueError ("Attributes are not set." )
221234 return self ._attributes
222235
223236 @property
@@ -241,7 +254,7 @@ def merge(self, other: "Resource") -> "Resource":
241254 Returns:
242255 The newly-created Resource.
243256 """
244- merged_attributes = self .attributes . copy ( )
257+ merged_attributes = dict ( self .attributes )
245258 merged_attributes .update (other .attributes )
246259
247260 if self .schema_url == "" :
@@ -257,7 +270,6 @@ def merge(self, other: "Resource") -> "Resource":
257270 other .schema_url ,
258271 )
259272 return self
260-
261273 return Resource (merged_attributes , schema_url )
262274
263275 def __eq__ (self , other : object ) -> bool :
@@ -268,15 +280,15 @@ def __eq__(self, other: object) -> bool:
268280 and self ._schema_url == other ._schema_url
269281 )
270282
271- def __hash__ (self ):
272- return hash (
273- f"{ dumps (self ._attributes .copy (), sort_keys = True )} |{ self ._schema_url } "
274- )
283+ def __hash__ (self ) -> int :
284+ attributes_json = dumps (self ._attributes .copy (), sort_keys = True )
285+ return hash (f"{ attributes_json } |{ self ._schema_url } " )
275286
276- def to_json (self , indent = 4 ) -> str :
287+ def to_json (self , indent : int = 4 ) -> str :
288+ attributes = dict (self ._attributes )
277289 return dumps (
278290 {
279- "attributes" : dict ( self . _attributes ) ,
291+ "attributes" : attributes ,
280292 "schema_url" : self ._schema_url ,
281293 },
282294 indent = indent ,
@@ -294,7 +306,7 @@ def to_json(self, indent=4) -> str:
294306
295307
296308class ResourceDetector (abc .ABC ):
297- def __init__ (self , raise_on_error = False ):
309+ def __init__ (self , raise_on_error : bool = False ) -> None :
298310 self .raise_on_error = raise_on_error
299311
300312 @abc .abstractmethod
@@ -343,7 +355,7 @@ def detect(self) -> "Resource":
343355 ),
344356 )
345357 )
346- _process_pid = os .getpid ()
358+ _process_pid = str ( os .getpid () )
347359 _process_executable_name = sys .executable
348360 _process_executable_path = os .path .dirname (_process_executable_name )
349361 _process_command = sys .argv [0 ]
@@ -358,23 +370,24 @@ def detect(self) -> "Resource":
358370 PROCESS_EXECUTABLE_PATH : _process_executable_path ,
359371 PROCESS_COMMAND : _process_command ,
360372 PROCESS_COMMAND_LINE : _process_command_line ,
361- PROCESS_COMMAND_ARGS : _process_command_args ,
373+ PROCESS_COMMAND_ARGS : "" . join ( _process_command_args ) ,
362374 }
363375 if hasattr (os , "getppid" ):
364376 # pypy3 does not have getppid()
365- resource_info [PROCESS_PARENT_PID ] = os .getppid ()
377+ resource_info [PROCESS_PARENT_PID ] = str ( os .getppid () )
366378
367379 if psutil is not None :
368380 process = psutil .Process ()
369- resource_info [PROCESS_OWNER ] = process .username ()
381+ username = cast (str , process .username ())
382+ resource_info [PROCESS_OWNER ] = username
370383
371384 return Resource (resource_info )
372385
373386
374387def get_aggregated_resources (
375388 detectors : typing .List ["ResourceDetector" ],
376389 initial_resource : typing .Optional [Resource ] = None ,
377- timeout = 5 ,
390+ timeout : int = 5 ,
378391) -> "Resource" :
379392 """Retrieves resources from detectors in the order that they were passed
380393
0 commit comments