Skip to content
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

Merged

Conversation

HiveTraum
Copy link
Contributor

Hi everyone!

My integration stuck with this little error. If to headers argument explicitly passed None value - it raises exception

@HiveTraum HiveTraum requested a review from a team as a code owner May 19, 2020 11:43
@codeboten
Copy link
Contributor

Hey @HiveTraum, thanks for the fix! You'll need to sign the CLA before it can be accepted.

@HiveTraum
Copy link
Contributor Author

I signed it

@HiveTraum
Copy link
Contributor Author

@codeboten , success, all checks passed

Copy link
Contributor

@codeboten codeboten left a 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.

@HiveTraum HiveTraum requested a review from codeboten May 21, 2020 09:12
Comment on lines +95 to +97
headers = kwargs.get("headers", {}) or {}
propagators.inject(type(headers).__setitem__, headers)
kwargs["headers"] = headers
Copy link
Contributor

@codeboten codeboten May 27, 2020

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:

Suggested change
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)

Copy link
Contributor Author

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

Copy link
Member

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

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Member

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

Copy link
Contributor Author

@HiveTraum HiveTraum Jun 8, 2020

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

Copy link
Member

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 ^

Copy link
Contributor

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

Copy link
Member

@toumorokoshi toumorokoshi left a 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())
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deleted this redudant instrumentation

Comment on lines +95 to +97
headers = kwargs.get("headers", {}) or {}
propagators.inject(type(headers).__setitem__, headers)
kwargs["headers"] = headers
Copy link
Member

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

Copy link
Member

@toumorokoshi toumorokoshi left a 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!

Comment on lines +95 to +97
headers = kwargs.get("headers", {}) or {}
propagators.inject(type(headers).__setitem__, headers)
kwargs["headers"] = headers
Copy link
Member

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

@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Jun 8, 2020

CLA Check
The committers are authorized under a signed CLA.

Copy link
Member

@toumorokoshi toumorokoshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying!

Comment on lines +95 to +97
headers = kwargs.get("headers", {}) or {}
propagators.inject(type(headers).__setitem__, headers)
kwargs["headers"] = headers
Copy link
Member

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 ^

@codeboten codeboten merged commit 93194e1 into open-telemetry:master Jun 9, 2020
codeboten pushed a commit to codeboten/opentelemetry-python that referenced this pull request Jun 11, 2020
srikanthccv pushed a commit to srikanthccv/opentelemetry-python that referenced this pull request Nov 1, 2020
* chore: simplify and speed up trace context parsing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants