-
Notifications
You must be signed in to change notification settings - Fork 656
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
requests headers none argument fix #708
requests headers none argument fix #708
Conversation
Hey @HiveTraum, thanks for the fix! You'll need to sign the CLA before it can be accepted. |
I signed it |
@codeboten , success, all checks passed |
ext/opentelemetry-ext-requests/src/opentelemetry/ext/requests/__init__.py
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution @HiveTraum . Just one question I'd like to get answered before approving.
headers = kwargs.get("headers", {}) or {} | ||
propagators.inject(type(headers).__setitem__, headers) | ||
kwargs["headers"] = headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry it took me this long to get back to you @HiveTraum, what I meant by my question was could this solve the problem instead of setting the headers attribute:
headers = kwargs.get("headers", {}) or {} | |
propagators.inject(type(headers).__setitem__, headers) | |
kwargs["headers"] = headers | |
headers = kwargs.get("headers", {}) | |
if headers is not None: | |
propagators.inject(type(headers).__setitem__, headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case request will not be traced, is that what we want? then it will be implicit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed that this approach should be taken.
Regarding the need for this PR at all: I guess requests does accept a None as headers. But looking at the API documentation, I don't see None as a valid value to pass in: https://requests.readthedocs.io/en/master/api/#requests.request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@codeboten
I might be missing something here, if the header is None
, we don't propagate the trace context but we still create the span? Would the trace just end there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's correct @lzchen, no trace would be propagated here. I guess this is a bit of a strange case, but if as a user I explicitly set headers to None
when sending a request, I'm not sure I would expect an instrumentation library to add a header regardless of my initial setting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case request will not be traced, is that what we want? then it will be implicit
@HiveTraum to clarify: propagators should not inject anything into headers if no such headers exist. At least, that's the standard that's used for every propagation scheme I know.
I think in this case it's fine, and I would argue more elegant than what's there now. Adding this skips the need to replace the headers entirely in the kwargs on line #97
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dataclass
class OrgStructureServiceEndpoint:
url: str
method: str = 'GET'
headers: dict = None
domain: str = None
params: dict = None
def send(self):
url = f"{self.domain or settings.ORGSTRUCT_URL}/{self.url}"
try:
response = requests.request(
method=self.method, url=url, headers=self.headers,
params=self.params, timeout=settings.REQUEST_TIMEOUT)
except requests.exceptions.Timeout:
raise APIException("Не удалось связаться с сервисом Оргструктуры")
I have such example which shows implicit scenario when user can pass None
headers implicitly. As user i want to see this request in traces.
I think that if user wants to send request without headers at all - suppress_instrumentation
must be used and headers=None
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, got it. Yeah I see that case now. Looking at the requests source code, it looks like things are handled in a very similar way (default to an empty dict):
def __init__(self,
method=None, url=None, headers=None, files=None, data=None,
params=None, auth=None, cookies=None, hooks=None, json=None):
# Default empty dicts for dict params.
data = [] if data is None else data
files = [] if files is None else files
headers = {} if headers is None else headers
params = {} if params is None else params
hooks = {} if hooks is None else hooks
So yes, this make sense. I'll switch to approved. Thanks!
@codeboten so you can see this too ^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for following up on this. Approving
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same changes that @codeboten is requesting. Happy to approve once that's addressed.
Thanks for the PR!
|
||
def test_if_headers_equals_none(self): | ||
RequestsInstrumentor().uninstrument() | ||
RequestsInstrumentor().instrument(tracer_provider=TracerProvider()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what purpose does clearing the instrumentor serve? by default the instrumentor is turned on, so I imagine the error would surface regardless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed with you, removed this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deleted this redudant instrumentation
headers = kwargs.get("headers", {}) or {} | ||
propagators.inject(type(headers).__setitem__, headers) | ||
kwargs["headers"] = headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed that this approach should be taken.
Regarding the need for this PR at all: I guess requests does accept a None as headers. But looking at the API documentation, I don't see None as a valid value to pass in: https://requests.readthedocs.io/en/master/api/#requests.request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@HiveTraum I see you've been making changes since the last review, which is great!
But I think the core of the original review is not yet addressed. see my new comment. That's the conversation we need to resolve before merging.
Thanks!
headers = kwargs.get("headers", {}) or {} | ||
propagators.inject(type(headers).__setitem__, headers) | ||
kwargs["headers"] = headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this case request will not be traced, is that what we want? then it will be implicit
@HiveTraum to clarify: propagators should not inject anything into headers if no such headers exist. At least, that's the standard that's used for every propagation scheme I know.
I think in this case it's fine, and I would argue more elegant than what's there now. Adding this skips the need to replace the headers entirely in the kwargs on line #97
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying!
headers = kwargs.get("headers", {}) or {} | ||
propagators.inject(type(headers).__setitem__, headers) | ||
kwargs["headers"] = headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, got it. Yeah I see that case now. Looking at the requests source code, it looks like things are handled in a very similar way (default to an empty dict):
def __init__(self,
method=None, url=None, headers=None, files=None, data=None,
params=None, auth=None, cookies=None, hooks=None, json=None):
# Default empty dicts for dict params.
data = [] if data is None else data
files = [] if files is None else files
headers = {} if headers is None else headers
params = {} if params is None else params
hooks = {} if hooks is None else hooks
So yes, this make sense. I'll switch to approved. Thanks!
@codeboten so you can see this too ^
* chore: simplify and speed up trace context parsing
Hi everyone!
My integration stuck with this little error. If to headers argument explicitly passed None value - it raises exception