8000 Do CLI import in __main__, not __init__ by natehardison · Pull Request #3547 · encode/httpx · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Do CLI import in __main__, not __init__ #3547

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

natehardison
Copy link

Summary

From #3523

Importing the _main.py module in httpx/__init__.py adds 0.087s worth of import time on my machine (MacBook M1 Pro, 32 GB RAM, macOS 15.3.1) running Python 3.13.2.

It's certainly not a huge amount of time in the absolute, but it roughly doubles the amount of time it takes to import httpx, even when you're not intending to run the CLI. Here's an import time graph generated using tuna on the output of python -X importtime -c "import httpx" > import.log

Screenshot 2025-03-03 at 4 15 23 PM

This PR adds httpx/__main__.py that does the import instead. This also allows python -m httpx as in #3216.

It does change the public API: httpx.main becomes the (more ungainly) httpx.__main__.main.

Checklist

  • I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.

8000
@natehardison natehardison force-pushed the master branch 6 times, most recently from 88baca0 to cccc8cb Compare April 8, 2025 23:54
@@ -59,7 +44,6 @@ def main() -> None: # type: ignore
"InvalidURL",
"Limits",
"LocalProtocolError",
"main",
Copy link
Contributor

Choose a reason for hiding this comment

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

If assume this causes breaking change (deletion from public api) we may need to keep it as pointer to __main__.main which also lazy import it:

def main():
    from .__main__ import main as orignial_main
    original_main()

by this way, there is no need to update test.

Copy link
Author

Choose a reason for hiding this comment

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

good point, will make this change

Copy link
Author

Choose a reason for hiding this comment

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

Turns out this is a little more complicated, unfortunately. The main function in httpx/_main.py is a click.BaseCommand, not just a typing.Callable. Wrapping it in a new def main(...) still changes the public API, since httpx.main is no longer a click.BaseCommand. This gets exposed by the tests, which attempt to access attributes of a click.BaseCommand object that aren't there for a simple function. To address, I believe we'd have to have httpx.__init__.py import click, which leads to the issue we're trying to get around in the first place, or we'd have to do some funky mocking magic to make main look more like a click.BaseCommand. I'm happy to explore that if that's the direction you'd like this to head, but doesn't feel quite right to me at this point.

In summary, I unfortunately can't think of a good way right now to maintain the current public API and achieve the goal of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Wrapping it in a new def main(...) still changes the public API

Also, it could use PEP562 (module level __getattr__) instead of wrapping:

def __getattr__(name):
    if name == "main":
        from .__main__ import main
        return main
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

Copy link
Author

Choose a reason for hiding this comment

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

Thanks, updated. Removed the __main__.py module to keep the diff as small as possible. Can add back, or it can go in a separate PR since it's a separate feature.

@natehardison natehardison force-pushed the master branch 4 times, most recently from 28a382b to fab0285 Compare April 15, 2025 21:34
Importing the CLI code doubles the import time for httpx altogether. By
importing lazily at runtime, the additional time only happens when
running the httpx CLI, and not on every "import httpx".
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.

2 participants
0