GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-91539: Small performance improvement of urrlib.request.getproxies_environment() by RaphaelMarinier · Pull Request #108771 · python/cpython

Conversation

@RaphaelMarinier

@RaphaelMarinier RaphaelMarinier commented Sep 1, 2023 •
edited by bedevere-bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

This is a small (10 - 20%) and trivial performance improvement of urrlib.request.getproxies_environment() , typically useful when there are many environment variables to go over.

This function is typically called each time one does a request using the requests package, and can become a bottleneck when there are 1000s of (unrelated) environment variables, so any performance improvement in this function is welcome.

This is an additional performance improvement to what was already done as part of #91539.

Performance details

With 100 unrelated env vars:
python getproxies_environment_benchmark.py 100

Walltime of each invocation of getproxies_environment() 0.057759761810302734 ms
Walltime of each invocation of getproxies_environment_faster() 0.04382586479187012 ms
Speedup: 24.12388240830176 %

And with 5000:

Walltime of each invocation of getproxies_environment() 1.234044075012207 ms
Walltime of each invocation of getproxies_environment_faster() 1.0999860763549805 ms
Speedup: 10.863307184218716 %

With the code below:

import os
import sys
import time


def fill_env_dummy_values(n_proxies=4, n_non_proxies=10):
    for i in range(n_non_proxies):
        os.environ[f'dummy_environ_var_{i}'] = '0' * 50
    for i in range(n_proxies):
        os.environ[f'scheme_{i}_proxy'] = '0' * 20


# https://github.com/python/cpython/blob/f8be2e262c5c2fdbc9721210ae1cb46edc16db82/Lib/urllib/request.py#L2479
def getproxies_environment():
    """Return a dictionary of scheme -> proxy server URL mappings.

    Scan the environment for variables named <scheme>_proxy;
    this seems to be the standard convention.  If you need a
    different way, you can pass a proxies dictionary to the
    [Fancy]URLopener constructor.
    """
    # in order to prefer lowercase variables, process environment in
    # two passes: first matches any, second pass matches lowercase only

    # select only environment variables which end in (after making lowercase) _proxy
    proxies = {}
    environment = []
    for name in os.environ.keys():
        # fast screen underscore position before more expensive case-folding
        if len(name) > 5 and name[-6] == "_" and name[-5:].lower() == "proxy":
            value = os.environ[name]
            proxy_name = name[:-6].lower()
            environment.append((name, value, proxy_name))
            if value:
                proxies[proxy_name] = value
    # CVE-2016-1000110 - If we are running as CGI script, forget HTTP_PROXY
    # (non-all-lowercase) as it may be set from the web server by a "Proxy:"
    # header from the client
    # If "proxy" is lowercase, it will still be used thanks to the next block
    if 'REQUEST_METHOD' in os.environ:
        proxies.pop('http', None)
    for name, value, proxy_name in environment:
        # not case-folded, checking here for lower-case env vars only
        if name[-6:] == '_proxy':
            if value:
                proxies[proxy_name] = value
            else:
                proxies.pop(proxy_name, None)
    return proxies


# Speedup of
# https://github.com/python/cpython/blob/f8be2e262c5c2fdbc9721210ae1cb46edc16db82/Lib/urllib/request.py#L2479
def getproxies_environment_faster():
    """Return a dictionary of scheme -> proxy server URL mappings.

    Scan the environment for variables named <scheme>_proxy;
    this seems to be the standard convention.  If you need a
    different way, you can pass a proxies dictionary to the
    [Fancy]URLopener constructor.
    """
    # in order to prefer lowercase variables, process environment in
    # two passes: first matches any, second pass matches lowercase only

    # select only environment variables which end in (after making lowercase) _proxy
    proxies = {}
    environment = []
    for name in os.environ:  # No .keys() here.
        # fast screen underscore position before more expensive case-folding
        if len(name) > 5 and name[-6] == "_" and name[-5:].lower() == "proxy":
            value = os.environ[name]
            proxy_name = name[:-6].lower()
            environment.append((name, value, proxy_name))
            if value:
                proxies[proxy_name] = value
    # CVE-2016-1000110 - If we are running as CGI script, forget HTTP_PROXY
    # (non-all-lowercase) as it may be set from the web server by a "Proxy:"
    # header from the client
    # If "proxy" is lowercase, it will still be used thanks to the next block
    if 'REQUEST_METHOD' in os.environ:
        proxies.pop('http', None)
    for name, value, proxy_name in environment:
        # not case-folded, checking here for lower-case env vars only
        if name[-6:] == '_proxy':
            if value:
                proxies[proxy_name] = value
            else:
                proxies.pop(proxy_name, None)
    return proxies


if __name__ == '__main__':
    fill_env_dummy_values(n_proxies=4, n_non_proxies=int(sys.argv[1]))

    # Make sure there is no pre-warming effect.
    getproxies_environment()
    getproxies_environment_faster()
    
    num_repeats = 1000
    t0 = time.time()
    for _ in range(num_repeats):
        getproxies_environment()
    getproxies_environment_time_per_call = (time.time() - t0) / num_repeats
    print('Walltime of each invocation of getproxies_environment()',
          getproxies_environment_time_per_call * 1000, 'ms')
    t0 = time.time()
    for _ in range(num_repeats):
        getproxies_environment_faster()
    getproxies_environment_faster_time_per_call = (time.time() - t0) / num_repeats
    print('Walltime of each invocation of getproxies_environment_faster()',
          getproxies_environment_faster_time_per_call * 1000, 'ms')
    print('Speedup:', (getproxies_environment_time_per_call - getproxies_environment_faster_time_per_call) / getproxies_environment_time_per_call * 100, '%')

  • Issue: speed up urllib.request.getproxies_environment #91539

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@RaphaelMarinier
Small performance improvement of getproxies_environment() when there …
b1d155f
…are many environment variables. In a benchmark with 5k environment variables not related to proxies, and 5 specifying proxies, we get a 10% walltime improvement.
@ghost

ghost commented Sep 1, 2023 •
edited by ghost
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.
CLA signed

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot mentioned this pull request Oct 5, 2022
speed up urllib.request.getproxies_environment #91539
Closed
@bedevere-bot

bedevere-bot commented Sep 1, 2023

Copy link
Copy Markdown

Most changes to Python require a NEWS entry.

Please add it using the blurb_it web app or the blurb command-line tool.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-bot bedevere-bot added the awaiting review label Sep 1, 2023
blurb-it Bot and others added 2 commits September 1, 2023 15:33
@blurb-it
📜🤖 Added by blurb_it.
0e43950
@RaphaelMarinier
Merge branch 'main' into speedup-getproxies-environment
54e48bf
eendebakpt
eendebakpt reviewed Sep 17, 2023
View reviewed changes
Comment thread Misc/NEWS.d/next/Library/2023-09-01-15-33-18.gh-issue-91539.xoNLEI.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

@eendebakpt

eendebakpt commented Sep 17, 2023

Copy link
Copy Markdown
Contributor

Small benchmark:

import os
import urllib.request

for ii in range(20):
    os.environ[f'x{ii}_proxy']='a'

%timeit urllib.request.getproxies_environment()

results in

main: 70.2 µs ± 185 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
PR: 66.3 µs ± 2.41 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

So there is indeed a small performance improvement.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eendebakpt
eendebakpt approved these changes Sep 17, 2023
View reviewed changes
@bedevere-app bedevere-app Bot added awaiting core review and removed awaiting review labels Sep 17, 2023
@hauntsaninja @eendebakpt
Update Misc/NEWS.d/next/Library/2023-09-01-15-33-18.gh-issue-91539.xo…
74f8537
…NLEI.rst

Co-authored-by: Pieter Eendebak <[email protected]>
hauntsaninja
hauntsaninja approved these changes Sep 18, 2023
View reviewed changes

@hauntsaninja hauntsaninja left a comment

Copy link
Copy Markdown
Contributor

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.

Thank you!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
RaphaelMarinier and others added 3 commits September 18, 2023 22:26
@RaphaelMarinier
Merge branch 'main' into speedup-getproxies-environment
f93f749
@RaphaelMarinier
Merge branch 'main' into speedup-getproxies-environment
53caf76
@hauntsaninja
Merge branch 'main' into speedup-getproxies-environment
0b57f48
@eendebakpt

eendebakpt commented Nov 13, 2023

Copy link
Copy Markdown
Contributor

Thank you!

@hauntsaninja is there anything we still need to do for this pr?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@hauntsaninja
hauntsaninja merged commit 5094690 into python:main Jan 15, 2024
@bedevere-app bedevere-app Bot removed the awaiting core review label Jan 15, 2024
kulikjak pushed a commit to kulikjak/cpython that referenced this pull request Jan 22, 2024
@RaphaelMarinier @kulikjak
pythongh-91539: Small performance improvement of urrlib.request.getpr…
ed5889d
…oxies_environment() (python#108771)

 Small performance improvement of getproxies_environment() when there are many environment variables. In a benchmark with 5k environment variables not related to proxies, and 5 specifying proxies, we get a 10% walltime improvement.
aisk pushed a commit to aisk/cpython that referenced this pull request Feb 11, 2024
@RaphaelMarinier @aisk
pythongh-91539: Small performance improvement of urrlib.request.getpr…
9717187
…oxies_environment() (python#108771)

 Small performance improvement of getproxies_environment() when there are many environment variables. In a benchmark with 5k environment variables not related to proxies, and 5 specifying proxies, we get a 10% walltime improvement.
Glyphack pushed a commit to Glyphack/cpython that referenced this pull request Sep 2, 2024
@RaphaelMarinier @Glyphack
pythongh-91539: Small performance improvement of urrlib.request.getpr…
e72ae11
…oxies_environment() (python#108771)

 Small performance improvement of getproxies_environment() when there are many environment variables. In a benchmark with 5k environment variables not related to proxies, and 5 specifying proxies, we get a 10% walltime improvement.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@eendebakpt eendebakpt eendebakpt approved these changes
@hauntsaninja hauntsaninja hauntsaninja approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@RaphaelMarinier @bedevere-bot @eendebakpt @hauntsaninja
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.