Skip to content

should not import optional dependencies until actually necessary #176

Description

@yarikoptic

while troubleshooting some awkward side effect of my app failing as far as I import tqdm, I discovered that tqdm unconditionally imports ipython (and probably other functionality if present, such as pandas) even if I have no intent on using it. It adds quite quite an overhead to the import, e.g.

with ipython installed/available

buildbot@922f7d817f64:~/build-custom$ time python -c 'import tqdm'

real    0m0.194s
user    0m0.168s
sys     0m0.020s

without:

(venv-ci) buildbot@922f7d817f64:~/build-custom$ time python -c 'import tqdm'

real    0m0.057s
user    0m0.040s
sys     0m0.012s

i.e. having ipython available contributes now almost 300% penalty
I guess ideally all those imports should happen only if related functionality is requested

Activity

  1. lrq3000 commented on Jun 2, 2016

    @lrq3000
    Member

    We 100% agree, but this is just how Python is designed: all imports in imported submodules in __init__ are executed when you import tqdm, whether or not you need them for the submodule you use...

    Maybe we can get around this issue by replacing static imports by dynamic imports in __init__, but I'm not sure this will be standard nor very compatible... We need to dig deeper into this issue since tqdm is becoming highly modular.

  2. yarikoptic commented on Jun 2, 2016

    @yarikoptic
    ContributorAuthor

    well -- that is the beauty of Python that it is so dynamic allowing for all kinds of wizardry (e.g. imports within functions, patching of the namespaces, etc ;-) ). E.g. see how I had to workaround (somewhat quick and dirty) this issue for ourselves: datalad/datalad@81e6113 which boils to delaying import of our progressbar adapters until they are actually used for the first time.

    In your case, I think ideal most Pythonic solution IMHO would have been not to dump everything into the top level API (i.e. __init__.py) and require something like from tqdm.ipython import tqdm_notebook. But if you are to stay with top level API providing all those, you could easily do it e.g. via somewhat ugly adapters #177

  3. yarikoptic commented on Jun 2, 2016

    @yarikoptic
    ContributorAuthor

    also not sure really if tqdm.tqdm_notebook is better than what could be more descriptive and avoiding c-style prefixing tqdm.ipython.notebook (if you do decide to change the API) ;-)

  4. lrq3000 commented on Jun 2, 2016

    @lrq3000
    Member

    I don't think changing the API would be better, as this would greatly reduce the ergonomics of tqdm. Your solution isn't that bad, thank you very much for the suggestion! An adapter may be the way to go here. Or lazy imports like with this library.

    /EDIT: tried it, seems to be a very good implementation of delayed imports in Python, but it does not support relative imports, so it doesn't work with tqdm for our purpose... Also, I'm not sure it works with Python 3, I guess it needs some update.

  5. self-assigned this
    on Jun 2, 2016
  6. casperdcl commented on Jun 4, 2016

    @casperdcl
    SponsorMember

    currently https://github.com/tqdm/tqdm/tree/lazydoc doesn't quite do it (thanks @yarikoptic for the suggestion)... tqdm.tqdm_notebook.__doc__ is accessible, but >>> help(tqdm.tqdm_notebook) won't pass that docstring through...

  7. yarikoptic commented on Jun 4, 2016

    @yarikoptic
    ContributorAuthor

    d'oh -- my ipython rotten soul forgot about pyhelp... indeed -- that one seems to be too smart for its own good. FWIW I have tried to see if we could trick it but unfortunately didn't find any easy way

  8. lrq3000 commented on Jun 7, 2016

    @lrq3000
    Member

    Sorry guys I'm a bit silent but I am really busy for the next two weeks so I can't contribute on this issue atm, but I'll check it out as soon as I'm out of the water with work.

  9. lrq3000 commented on Jul 3, 2016

    @lrq3000
    Member

    Found another issue with current solution for tqdm_notebook using a delayed adapter: class methods just don't work, for example tqdm_notebook.write("hello") outputs the following error:

    AttributeError: 'function' object has no attribute 'write'

    See #188.

    So maybe we should just scrap the __all__ in __init__.py and reorganize the whole tqdm lib. Here is a suggestion from @wernight in #188:

    # Do one of:
    import tqdm.console as tqdm
    import tqdm.gui as tqdm
    import tqdm.notebook as tqdm
    
    # All have same interface:
    tqdm.trange(...)
    tqdm.tqdm(...)
    tqdm.write(...)
    

    If we want to provide something similar to the old interface, we could also do with the same architecture:

    from tqdm.console import tqdm, trange
    
    for i in trange(10):
        etc.
    

    What do you think guys? Should we continue with delayed imports or just reorganize tqdm in a more modular fashion? (of course, in the latter case, we would break the API, so we would need to release a major release...).

  10. lrq3000 commented on Jul 4, 2016

    @lrq3000
    Member

    An alternative to doing a full architecture reorganization, while keeping __all__ in __init__.py: we could make it standard that a wrapper function needs to be called to fetch the target tqdm class (code not tested):

    # in __init__.py
    def tqdm_load():
        from tqdm import tqdm
        return tqdm
    
    # then in any script
    from tqdm import tqdm_load
    tqdm_class = tqdm_load()
    t = tqdm_class()
    tqdm_class.write("hello")
    t.write("hello")
    

    Pro is that we retain all features and a pretty much similar API to what we have now but class methods and all functionalities should work as expected, con is obviously that it's quite non-pythonic and ugly to use.

  11. added a commit that references this issue on Oct 31, 2016
    8c2c88f
  12. added this to the milestone on Feb 26, 2018
  13. casperdcl commented on Aug 5, 2022

    @casperdcl
    SponsorMember

    Note: language-level lazy imports PEP#0690 land in Python 3.12 🤞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions