2020from packaging .requirements import InvalidRequirement , Requirement
2121
2222from opentelemetry .util ._importlib_metadata import (
23+ Distribution ,
2324 PackageNotFoundError ,
2425 version ,
2526)
2627
2728logger = getLogger (__name__ )
2829
30+ #TODO: consider replacing _either with _any or _or
2931
3032class DependencyConflict :
3133 required : str | None = None
3234 found : str | None = None
35+ # The following fields are used when an instrumentation requires any of a set of dependencies rather than all.
36+ required_either : Collection [str ] = []
37+ found_either : Collection [str ] = []
3338
34- def __init__ (self , required : str | None , found : str | None = None ):
39+ # TODO: No longer requires required field
40+ def __init__ (self , required : str | None = None , found : str | None = None , required_either : Collection [str ] = [], found_either : Collection [str ] = []):
3541 self .required = required
3642 self .found = found
43+ # The following fields are used when an instrumentation requires any of a set of dependencies rather than all.
44+ self .required_either = required_either
45+ self .found_either = found_either
3746
3847 def __str__ (self ):
48+ if not self .required and (self .required_either or self .found_either ):
49+ # TODO: make sure this formats correctly
50+ return f'DependencyConflict: requested any of the following: "{ self .required_either } " but found: "{ self .found_either } "'
3951 return f'DependencyConflict: requested: "{ self .required } " but found: "{ self .found } "'
4052
53+ # TODO: Figure out if this should be a subclass of DependencyConflict.
54+ # If now, change functions to return either and then ensure that all the dependents can handle the new value
55+ # class DependencyConflictEither(DependencyConflict):
56+ # required: Collection[str] = []
57+ # found: Collection[str] = []
58+
59+ # def __init__(self, required: Collection[str], found: Collection[str] = []):
60+ # self.required = required
61+ # self.found = found
62+
63+ # def __str__(self):
64+ # return f'DependencyConflictEither: requested: "{self.required}" but found: "{self.found}"'
65+
4166
4267class DependencyConflictError (Exception ):
4368 conflict : DependencyConflict
@@ -49,10 +74,49 @@ def __str__(self):
4974 return str (self .conflict )
5075
5176
77+ def get_dist_dependency_conflicts (
78+ dist : Distribution ,
79+ ) -> DependencyConflict | None :
80+ instrumentation_deps = []
81+ instrumentation_either_deps = []
82+ extra = "extra"
83+ instruments = "instruments"
84+ instruments_marker = {extra : instruments }
85+ instruments_either = "instruments_either"
86+ instruments_either_marker = {extra : instruments_either }
87+ print (f"dist: { dist } " )
88+ print (f"dist.requires: { dist .requires } " )
89+ if dist .requires :
90+ for dep in dist .requires :
91+ print (f"dep: { dep } " )
92+ if extra not in dep :
93+ print (f"Skipping dep: { dep } " )
94+ continue
95+ if instruments not in dep and instruments_either not in dep :
96+ print (f"Skipping dep: { dep } " )
97+ continue
98+
99+ req = Requirement (dep )
100+ print (f"req: { req } " )
101+ if req .marker .evaluate (instruments_marker ): # type: ignore
102+ print ("Evaluated. Append" )
103+ instrumentation_deps .append (req ) # type: ignore
104+ if req .marker .evaluate (instruments_either_marker ): # type: ignore
105+ print ("Evaluated. either. Append" )
106+ # Need someway to separate
107+ instrumentation_either_deps .append (req ) # type: ignore
108+ dc = get_dependency_conflicts (instrumentation_deps , instrumentation_either_deps ) # type: ignore
109+ print (f"dep conf: { dc } " )
110+ return dc
111+ # return get_dependency_conflicts(instrumentation_deps, instrumentation_either_deps) # type: ignore
112+
113+
52114def get_dependency_conflicts (
53- deps : Collection [str | Requirement ],
115+ deps : Collection [str | Requirement ], # Depedencies all of which are required
116+ deps_either : Collection [str | Requirement ] = [], # Dependencies any of which are required
54117) -> DependencyConflict | None :
55118 for dep in deps :
119+ # TODO: what is this?
56120 if isinstance (dep , Requirement ):
57121 req = dep
58122 else :
@@ -69,8 +133,55 @@ def get_dependency_conflicts(
69133 try :
70134 dist_version = version (req .name )
71135 except PackageNotFoundError :
136+ # TODO: Technically this field should allow Requirements type. Tackle this in a separate PR.
72137 return DependencyConflict (dep )
73138
74139 if not req .specifier .contains (dist_version ):
140+ # TODO: Technically this field should allow Requirements type
75141 return DependencyConflict (dep , f"{ req .name } { dist_version } " )
142+
143+ # TODO: add eval of deps_either
144+ if deps_either :
145+ # TODO: change to using DependencyConflict
146+ # is_dependency_conflict = True
147+ required_either : Collection [str ] = []
148+ found_either : Collection [str ] = []
149+ for dep in deps_either :
150+ # TODO: what is this?
151+ if isinstance (dep , Requirement ):
152+ req = dep
153+ else :
154+ try :
155+ req = Requirement (dep )
156+ except InvalidRequirement as exc :
157+ logger .warning (
158+ 'error parsing dependency, reporting as a conflict: "%s" - %s' ,
159+ dep ,
160+ exc ,
161+ )
162+ return DependencyConflict (dep )
163+
164+ try :
165+ dist_version = version (req .name )
166+ except PackageNotFoundError :
167+ print (f"PackageNotFoundError: { req .name } " )
168+ continue
169+ # TODO: anything here?
170+ # return DependencyConflict(dep)
171+ required_either .append (dep )
172+
173+ if req .specifier .contains (dist_version ):
174+ # is_dependency_conflict = False
175+ # Since only one of the instrumentation_either dependencies is required, there is no depdendency conflict.
176+ break
177+ else :
178+ required_either .append (dep )
179+ found_either .append (f"{ req .name } { dist_version } " )
180+
181+ # return DependencyConflict(dep, f"{req.name} {dist_version}")
182+ return DependencyConflict (
183+ required_either = required_either ,
184+ found_either = found_either ,
185+ )
186+
76187 return None
0 commit comments