Skipping over failed imports until they are needed (if ever)Create virtual host + MySQL toolPython Decorator - inspecting function argument valuesImplementing Multi-File Programs in VitsySimple import system for a large shell programFile selection button for Jupyter notebookCode to fill PE import address tableMaking a graph of the import structure of a programCalculate various shapes Area and PerimeterPolymorphic Data Socket Factory with Query Contingency Protocols in PythonLogging with decorators (passing log file name as argument)

What does grep -v "grep" mean and do?

Should I report a leak of confidential HR information?

Can I travel from Germany to England alone as an unaccompanied minor?

What is "oversubscription" in Networking?

Why are there so many religions and gods?

Wrong corporate name on employment agreement

How to expand abbrevs without hitting another extra key?

Can Access Fault Exceptions of the MC68040 caused by internal access faults occur in normal situations?

Golf the smallest circle!

What are good ways to spray paint a QR code on a footpath?

Generate and graph the Recamán Sequence

What does Mildred mean by this line in Three Billboards Outside Ebbing, Missouri?

3D nonogram, beginner's edition

Other than an exponent greater than 1, what are the considerations when creating a random 256-bit private key using Diffie Hellman?

Can 'leave' mean 'forget'?

How to fix a dry solder pin in BGA package?

Prime parity peregrination

Could human civilization live 150 years in a nuclear-powered aircraft carrier colony without resorting to mass killing/ cannibalism?

One folder two different locations on ubuntu 18.04

Is this hogweed?

Is there a way for presidents to legally extend their terms beyond the maximum of four years?

Is there a nice way to assign std::minmax(a, b) to std::tie(a, b)?

Is there reliable evidence that depleted uranium from the 1999 NATO bombing is causing cancer in Serbia?

Questions about authorship rank and academic politics



Skipping over failed imports until they are needed (if ever)


Create virtual host + MySQL toolPython Decorator - inspecting function argument valuesImplementing Multi-File Programs in VitsySimple import system for a large shell programFile selection button for Jupyter notebookCode to fill PE import address tableMaking a graph of the import structure of a programCalculate various shapes Area and PerimeterPolymorphic Data Socket Factory with Query Contingency Protocols in PythonLogging with decorators (passing log file name as argument)






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








5












$begingroup$


We have a program with many dependencies. Because the user may not want to use all of them, the program should skip over failed imports and only raise an error if absolutely necessary. e.g. if the user tries to call that class.



I have implemented a way of doing this but I'm not sure it is as good as it could be, it certainly seems somewhat inelegant. I presume there are also some overlooked bugs.



The important file structure is:



Program:
- engines
- __init__.py
- base_engine.py
- ...
- psi4.py
- rdkit.py
- run.py


The base_engine.py contains the base class Engines which all the other engines (PSI4 in psi4.py, RDKit in rdkit.py ... ) inherit from, as well as a Default class.



If any class fails to import, Default is imported instead, but with the name of the class that failed to import (see the __init__.py file).



base_engine.py:



class Engines:
"""
Engines base class containing core information that all other engines (PSI4, etc) will have.
"""

def __init__(self, molecule):

self.molecule = molecule


class Default:
"""
If there is an import error, this class replaces the class which failed to be imported.
Then, only if initialised, an import error will be raised notifying the user of a failed call.
"""

def __init__(self, *args, **kwargs):
# self.name is set when the failed-to-import class is set to Default.
raise ImportError(
f'The class self.name you tried to call is not importable; '
f'this is likely due to it not being installed.')


__init__.py:



try:
from .psi4 import PSI4
except ImportError:
from .base_engine import Default as PSI4
setattr(PSI4, 'name', 'PSI4')

try:
from .rdkit import RDKit
except ImportError:
from .base_engine import Default as RDKit
setattr(RDKit, 'name', 'RDKit')


psi4.py:



from Program.engines.base_engine import Engines

# Example import that would fail
import abcdefg


class PSI4(Engines):
"""
Writes and executes input files for psi4.
"""

def __init__(self, molecule):

super().__init__(molecule)

def generate_input(self):

...


run.py:



from Program.engines import PSI4

PSI4('example_molecule').generate_input()


So now when classes are imported at the top of the run.py file, there is no problem even if there's an import error; if there's a failed import with PSI4 because abcdefg cannot be imported, Default is simply imported as PSI4 and given the name PSI4 via the setattr().



Then, only if that Default class is called the ImportError is raised and the user can see that the issue was with PSI4.



This seems to work quite well, even when there are multiple failed imports. We can also add extended error messages for each different failed import. Is this the best way of doing something like this, though? It gets quite messy since we have so many files in our engines package.



Please let me know if some relevant code has been omitted and I can add it back. Thanks for any help.










share|improve this question











$endgroup$


















    5












    $begingroup$


    We have a program with many dependencies. Because the user may not want to use all of them, the program should skip over failed imports and only raise an error if absolutely necessary. e.g. if the user tries to call that class.



    I have implemented a way of doing this but I'm not sure it is as good as it could be, it certainly seems somewhat inelegant. I presume there are also some overlooked bugs.



    The important file structure is:



    Program:
    - engines
    - __init__.py
    - base_engine.py
    - ...
    - psi4.py
    - rdkit.py
    - run.py


    The base_engine.py contains the base class Engines which all the other engines (PSI4 in psi4.py, RDKit in rdkit.py ... ) inherit from, as well as a Default class.



    If any class fails to import, Default is imported instead, but with the name of the class that failed to import (see the __init__.py file).



    base_engine.py:



    class Engines:
    """
    Engines base class containing core information that all other engines (PSI4, etc) will have.
    """

    def __init__(self, molecule):

    self.molecule = molecule


    class Default:
    """
    If there is an import error, this class replaces the class which failed to be imported.
    Then, only if initialised, an import error will be raised notifying the user of a failed call.
    """

    def __init__(self, *args, **kwargs):
    # self.name is set when the failed-to-import class is set to Default.
    raise ImportError(
    f'The class self.name you tried to call is not importable; '
    f'this is likely due to it not being installed.')


    __init__.py:



    try:
    from .psi4 import PSI4
    except ImportError:
    from .base_engine import Default as PSI4
    setattr(PSI4, 'name', 'PSI4')

    try:
    from .rdkit import RDKit
    except ImportError:
    from .base_engine import Default as RDKit
    setattr(RDKit, 'name', 'RDKit')


    psi4.py:



    from Program.engines.base_engine import Engines

    # Example import that would fail
    import abcdefg


    class PSI4(Engines):
    """
    Writes and executes input files for psi4.
    """

    def __init__(self, molecule):

    super().__init__(molecule)

    def generate_input(self):

    ...


    run.py:



    from Program.engines import PSI4

    PSI4('example_molecule').generate_input()


    So now when classes are imported at the top of the run.py file, there is no problem even if there's an import error; if there's a failed import with PSI4 because abcdefg cannot be imported, Default is simply imported as PSI4 and given the name PSI4 via the setattr().



    Then, only if that Default class is called the ImportError is raised and the user can see that the issue was with PSI4.



    This seems to work quite well, even when there are multiple failed imports. We can also add extended error messages for each different failed import. Is this the best way of doing something like this, though? It gets quite messy since we have so many files in our engines package.



    Please let me know if some relevant code has been omitted and I can add it back. Thanks for any help.










    share|improve this question











    $endgroup$














      5












      5








      5


      0



      $begingroup$


      We have a program with many dependencies. Because the user may not want to use all of them, the program should skip over failed imports and only raise an error if absolutely necessary. e.g. if the user tries to call that class.



      I have implemented a way of doing this but I'm not sure it is as good as it could be, it certainly seems somewhat inelegant. I presume there are also some overlooked bugs.



      The important file structure is:



      Program:
      - engines
      - __init__.py
      - base_engine.py
      - ...
      - psi4.py
      - rdkit.py
      - run.py


      The base_engine.py contains the base class Engines which all the other engines (PSI4 in psi4.py, RDKit in rdkit.py ... ) inherit from, as well as a Default class.



      If any class fails to import, Default is imported instead, but with the name of the class that failed to import (see the __init__.py file).



      base_engine.py:



      class Engines:
      """
      Engines base class containing core information that all other engines (PSI4, etc) will have.
      """

      def __init__(self, molecule):

      self.molecule = molecule


      class Default:
      """
      If there is an import error, this class replaces the class which failed to be imported.
      Then, only if initialised, an import error will be raised notifying the user of a failed call.
      """

      def __init__(self, *args, **kwargs):
      # self.name is set when the failed-to-import class is set to Default.
      raise ImportError(
      f'The class self.name you tried to call is not importable; '
      f'this is likely due to it not being installed.')


      __init__.py:



      try:
      from .psi4 import PSI4
      except ImportError:
      from .base_engine import Default as PSI4
      setattr(PSI4, 'name', 'PSI4')

      try:
      from .rdkit import RDKit
      except ImportError:
      from .base_engine import Default as RDKit
      setattr(RDKit, 'name', 'RDKit')


      psi4.py:



      from Program.engines.base_engine import Engines

      # Example import that would fail
      import abcdefg


      class PSI4(Engines):
      """
      Writes and executes input files for psi4.
      """

      def __init__(self, molecule):

      super().__init__(molecule)

      def generate_input(self):

      ...


      run.py:



      from Program.engines import PSI4

      PSI4('example_molecule').generate_input()


      So now when classes are imported at the top of the run.py file, there is no problem even if there's an import error; if there's a failed import with PSI4 because abcdefg cannot be imported, Default is simply imported as PSI4 and given the name PSI4 via the setattr().



      Then, only if that Default class is called the ImportError is raised and the user can see that the issue was with PSI4.



      This seems to work quite well, even when there are multiple failed imports. We can also add extended error messages for each different failed import. Is this the best way of doing something like this, though? It gets quite messy since we have so many files in our engines package.



      Please let me know if some relevant code has been omitted and I can add it back. Thanks for any help.










      share|improve this question











      $endgroup$




      We have a program with many dependencies. Because the user may not want to use all of them, the program should skip over failed imports and only raise an error if absolutely necessary. e.g. if the user tries to call that class.



      I have implemented a way of doing this but I'm not sure it is as good as it could be, it certainly seems somewhat inelegant. I presume there are also some overlooked bugs.



      The important file structure is:



      Program:
      - engines
      - __init__.py
      - base_engine.py
      - ...
      - psi4.py
      - rdkit.py
      - run.py


      The base_engine.py contains the base class Engines which all the other engines (PSI4 in psi4.py, RDKit in rdkit.py ... ) inherit from, as well as a Default class.



      If any class fails to import, Default is imported instead, but with the name of the class that failed to import (see the __init__.py file).



      base_engine.py:



      class Engines:
      """
      Engines base class containing core information that all other engines (PSI4, etc) will have.
      """

      def __init__(self, molecule):

      self.molecule = molecule


      class Default:
      """
      If there is an import error, this class replaces the class which failed to be imported.
      Then, only if initialised, an import error will be raised notifying the user of a failed call.
      """

      def __init__(self, *args, **kwargs):
      # self.name is set when the failed-to-import class is set to Default.
      raise ImportError(
      f'The class self.name you tried to call is not importable; '
      f'this is likely due to it not being installed.')


      __init__.py:



      try:
      from .psi4 import PSI4
      except ImportError:
      from .base_engine import Default as PSI4
      setattr(PSI4, 'name', 'PSI4')

      try:
      from .rdkit import RDKit
      except ImportError:
      from .base_engine import Default as RDKit
      setattr(RDKit, 'name', 'RDKit')


      psi4.py:



      from Program.engines.base_engine import Engines

      # Example import that would fail
      import abcdefg


      class PSI4(Engines):
      """
      Writes and executes input files for psi4.
      """

      def __init__(self, molecule):

      super().__init__(molecule)

      def generate_input(self):

      ...


      run.py:



      from Program.engines import PSI4

      PSI4('example_molecule').generate_input()


      So now when classes are imported at the top of the run.py file, there is no problem even if there's an import error; if there's a failed import with PSI4 because abcdefg cannot be imported, Default is simply imported as PSI4 and given the name PSI4 via the setattr().



      Then, only if that Default class is called the ImportError is raised and the user can see that the issue was with PSI4.



      This seems to work quite well, even when there are multiple failed imports. We can also add extended error messages for each different failed import. Is this the best way of doing something like this, though? It gets quite messy since we have so many files in our engines package.



      Please let me know if some relevant code has been omitted and I can add it back. Thanks for any help.







      python python-3.x object-oriented error-handling dynamic-loading






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 7 hours ago









      200_success

      134k21 gold badges169 silver badges440 bronze badges




      134k21 gold badges169 silver badges440 bronze badges










      asked 9 hours ago









      HoboProberHoboProber

      79710 bronze badges




      79710 bronze badges




















          3 Answers
          3






          active

          oldest

          votes


















          3












          $begingroup$

          There is one issue with your approach which is, in the case of multiple import failure, only the last one can be properly reported:



          >>> try:
          ... from .foo import Foo
          ... except ImportError:
          ... from .base_engine import Default as Foo
          ... setattr(Foo, 'name', 'Foo')
          ...
          >>> try:
          ... from .bar import Bar
          ... except ImportError:
          ... from .base_engine import Default as Bar
          ... setattr(Bar, 'name', 'Bar')
          ...
          >>> Bar()
          Traceback (most recent call last):

          ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.
          >>> Foo()
          Traceback (most recent call last):

          ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.


          You instead want to generate a new class with the proper message each time.



          Something along the line should do:



          def mock_missing(name):
          def init(self, *args, **kwargs):
          raise ImportError(
          f'The class name you tried to call is not importable; '
          f'this is likely due to it not being installed.')
          return type(name, (), '__init__': init)


          Usage being:



          try:
          from .foo import Foo
          except ImportError:
          Foo = mock_missing('Foo')





          share|improve this answer









          $endgroup$




















            2












            $begingroup$

            That's an odd use of the word "default", which, etymologically speaking, means to remove failure, and generally refers to a fallback setting that "just works". I suggest calling it Missing instead.






            share|improve this answer









            $endgroup$




















              1












              $begingroup$

              This answer builds upon the solution presented by Mathias Ettinger




              If you want to get rid of the try: ... except: ... boilerplate you could implement some kind of custom loader for your engines.



              This is something I wrote quickly, which is likely far from perfect, but it works.



              in __init__.py:



              import importlib


              def mock_missing(name, msg):
              def init(self, *args, **kwargs):
              raise ImportError(
              f'The class name you tried to call is not importable; '
              f'this is likely due to it not being installed. '
              f'Original reason: msg')
              return type(name, (), '__init__': init)


              def try_load(engine, module):
              """Try to load an engine, return a mock class if it was not found

              Inspired by https://stackoverflow.com/a/10675081
              """
              try:
              module = importlib.import_module(module, __name__)
              return getattr(module, engine)
              except (ModuleNotFoundError, AttributeError) as ex:
              return mock_missing(engine, msg=str(ex))


              To test it:



              from engines import try_load

              PSI4 = try_load("PSI4", ".psi4")
              a = PSI4()

              # module does not exist
              try:
              PSI5 = try_load("PSI5", ".PSI5")
              b = PSI5()
              raise AssertionError("This should have failed!")
              except ImportError as ex:
              print(str(ex))

              # class does not exist in module
              try:
              PSI6 = try_load("PSI6", ".psi4")
              c = PSI6()
              raise AssertionError("This should have failed!")
              except ImportError as ex:
              print(str(ex))



              This outputs:



              The class PSI5 you tried to call is not importable; this is likely due to it not being installed. Original reason: No module named 'engines.psi5'
              The class PSI6 you tried to call is not importable; this is likely due to it not being installed. Original reason: module 'engines.psi4' has no attribute 'PSI6'





              share|improve this answer









              $endgroup$















                Your Answer






                StackExchange.ifUsing("editor", function ()
                StackExchange.using("externalEditor", function ()
                StackExchange.using("snippets", function ()
                StackExchange.snippets.init();
                );
                );
                , "code-snippets");

                StackExchange.ready(function()
                var channelOptions =
                tags: "".split(" "),
                id: "196"
                ;
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function()
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled)
                StackExchange.using("snippets", function()
                createEditor();
                );

                else
                createEditor();

                );

                function createEditor()
                StackExchange.prepareEditor(
                heartbeatType: 'answer',
                autoActivateHeartbeat: false,
                convertImagesToLinks: false,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: null,
                bindNavPrevention: true,
                postfix: "",
                imageUploader:
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                ,
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                );



                );













                draft saved

                draft discarded


















                StackExchange.ready(
                function ()
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f222872%2fskipping-over-failed-imports-until-they-are-needed-if-ever%23new-answer', 'question_page');

                );

                Post as a guest















                Required, but never shown

























                3 Answers
                3






                active

                oldest

                votes








                3 Answers
                3






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                3












                $begingroup$

                There is one issue with your approach which is, in the case of multiple import failure, only the last one can be properly reported:



                >>> try:
                ... from .foo import Foo
                ... except ImportError:
                ... from .base_engine import Default as Foo
                ... setattr(Foo, 'name', 'Foo')
                ...
                >>> try:
                ... from .bar import Bar
                ... except ImportError:
                ... from .base_engine import Default as Bar
                ... setattr(Bar, 'name', 'Bar')
                ...
                >>> Bar()
                Traceback (most recent call last):

                ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.
                >>> Foo()
                Traceback (most recent call last):

                ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.


                You instead want to generate a new class with the proper message each time.



                Something along the line should do:



                def mock_missing(name):
                def init(self, *args, **kwargs):
                raise ImportError(
                f'The class name you tried to call is not importable; '
                f'this is likely due to it not being installed.')
                return type(name, (), '__init__': init)


                Usage being:



                try:
                from .foo import Foo
                except ImportError:
                Foo = mock_missing('Foo')





                share|improve this answer









                $endgroup$

















                  3












                  $begingroup$

                  There is one issue with your approach which is, in the case of multiple import failure, only the last one can be properly reported:



                  >>> try:
                  ... from .foo import Foo
                  ... except ImportError:
                  ... from .base_engine import Default as Foo
                  ... setattr(Foo, 'name', 'Foo')
                  ...
                  >>> try:
                  ... from .bar import Bar
                  ... except ImportError:
                  ... from .base_engine import Default as Bar
                  ... setattr(Bar, 'name', 'Bar')
                  ...
                  >>> Bar()
                  Traceback (most recent call last):

                  ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.
                  >>> Foo()
                  Traceback (most recent call last):

                  ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.


                  You instead want to generate a new class with the proper message each time.



                  Something along the line should do:



                  def mock_missing(name):
                  def init(self, *args, **kwargs):
                  raise ImportError(
                  f'The class name you tried to call is not importable; '
                  f'this is likely due to it not being installed.')
                  return type(name, (), '__init__': init)


                  Usage being:



                  try:
                  from .foo import Foo
                  except ImportError:
                  Foo = mock_missing('Foo')





                  share|improve this answer









                  $endgroup$















                    3












                    3








                    3





                    $begingroup$

                    There is one issue with your approach which is, in the case of multiple import failure, only the last one can be properly reported:



                    >>> try:
                    ... from .foo import Foo
                    ... except ImportError:
                    ... from .base_engine import Default as Foo
                    ... setattr(Foo, 'name', 'Foo')
                    ...
                    >>> try:
                    ... from .bar import Bar
                    ... except ImportError:
                    ... from .base_engine import Default as Bar
                    ... setattr(Bar, 'name', 'Bar')
                    ...
                    >>> Bar()
                    Traceback (most recent call last):

                    ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.
                    >>> Foo()
                    Traceback (most recent call last):

                    ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.


                    You instead want to generate a new class with the proper message each time.



                    Something along the line should do:



                    def mock_missing(name):
                    def init(self, *args, **kwargs):
                    raise ImportError(
                    f'The class name you tried to call is not importable; '
                    f'this is likely due to it not being installed.')
                    return type(name, (), '__init__': init)


                    Usage being:



                    try:
                    from .foo import Foo
                    except ImportError:
                    Foo = mock_missing('Foo')





                    share|improve this answer









                    $endgroup$



                    There is one issue with your approach which is, in the case of multiple import failure, only the last one can be properly reported:



                    >>> try:
                    ... from .foo import Foo
                    ... except ImportError:
                    ... from .base_engine import Default as Foo
                    ... setattr(Foo, 'name', 'Foo')
                    ...
                    >>> try:
                    ... from .bar import Bar
                    ... except ImportError:
                    ... from .base_engine import Default as Bar
                    ... setattr(Bar, 'name', 'Bar')
                    ...
                    >>> Bar()
                    Traceback (most recent call last):

                    ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.
                    >>> Foo()
                    Traceback (most recent call last):

                    ImportError: The class Bar you tried to call is not importable; this is likely due to it not being installed.


                    You instead want to generate a new class with the proper message each time.



                    Something along the line should do:



                    def mock_missing(name):
                    def init(self, *args, **kwargs):
                    raise ImportError(
                    f'The class name you tried to call is not importable; '
                    f'this is likely due to it not being installed.')
                    return type(name, (), '__init__': init)


                    Usage being:



                    try:
                    from .foo import Foo
                    except ImportError:
                    Foo = mock_missing('Foo')






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered 8 hours ago









                    Mathias EttingerMathias Ettinger

                    25.7k3 gold badges34 silver badges87 bronze badges




                    25.7k3 gold badges34 silver badges87 bronze badges























                        2












                        $begingroup$

                        That's an odd use of the word "default", which, etymologically speaking, means to remove failure, and generally refers to a fallback setting that "just works". I suggest calling it Missing instead.






                        share|improve this answer









                        $endgroup$

















                          2












                          $begingroup$

                          That's an odd use of the word "default", which, etymologically speaking, means to remove failure, and generally refers to a fallback setting that "just works". I suggest calling it Missing instead.






                          share|improve this answer









                          $endgroup$















                            2












                            2








                            2





                            $begingroup$

                            That's an odd use of the word "default", which, etymologically speaking, means to remove failure, and generally refers to a fallback setting that "just works". I suggest calling it Missing instead.






                            share|improve this answer









                            $endgroup$



                            That's an odd use of the word "default", which, etymologically speaking, means to remove failure, and generally refers to a fallback setting that "just works". I suggest calling it Missing instead.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 6 hours ago









                            200_success200_success

                            134k21 gold badges169 silver badges440 bronze badges




                            134k21 gold badges169 silver badges440 bronze badges





















                                1












                                $begingroup$

                                This answer builds upon the solution presented by Mathias Ettinger




                                If you want to get rid of the try: ... except: ... boilerplate you could implement some kind of custom loader for your engines.



                                This is something I wrote quickly, which is likely far from perfect, but it works.



                                in __init__.py:



                                import importlib


                                def mock_missing(name, msg):
                                def init(self, *args, **kwargs):
                                raise ImportError(
                                f'The class name you tried to call is not importable; '
                                f'this is likely due to it not being installed. '
                                f'Original reason: msg')
                                return type(name, (), '__init__': init)


                                def try_load(engine, module):
                                """Try to load an engine, return a mock class if it was not found

                                Inspired by https://stackoverflow.com/a/10675081
                                """
                                try:
                                module = importlib.import_module(module, __name__)
                                return getattr(module, engine)
                                except (ModuleNotFoundError, AttributeError) as ex:
                                return mock_missing(engine, msg=str(ex))


                                To test it:



                                from engines import try_load

                                PSI4 = try_load("PSI4", ".psi4")
                                a = PSI4()

                                # module does not exist
                                try:
                                PSI5 = try_load("PSI5", ".PSI5")
                                b = PSI5()
                                raise AssertionError("This should have failed!")
                                except ImportError as ex:
                                print(str(ex))

                                # class does not exist in module
                                try:
                                PSI6 = try_load("PSI6", ".psi4")
                                c = PSI6()
                                raise AssertionError("This should have failed!")
                                except ImportError as ex:
                                print(str(ex))



                                This outputs:



                                The class PSI5 you tried to call is not importable; this is likely due to it not being installed. Original reason: No module named 'engines.psi5'
                                The class PSI6 you tried to call is not importable; this is likely due to it not being installed. Original reason: module 'engines.psi4' has no attribute 'PSI6'





                                share|improve this answer









                                $endgroup$

















                                  1












                                  $begingroup$

                                  This answer builds upon the solution presented by Mathias Ettinger




                                  If you want to get rid of the try: ... except: ... boilerplate you could implement some kind of custom loader for your engines.



                                  This is something I wrote quickly, which is likely far from perfect, but it works.



                                  in __init__.py:



                                  import importlib


                                  def mock_missing(name, msg):
                                  def init(self, *args, **kwargs):
                                  raise ImportError(
                                  f'The class name you tried to call is not importable; '
                                  f'this is likely due to it not being installed. '
                                  f'Original reason: msg')
                                  return type(name, (), '__init__': init)


                                  def try_load(engine, module):
                                  """Try to load an engine, return a mock class if it was not found

                                  Inspired by https://stackoverflow.com/a/10675081
                                  """
                                  try:
                                  module = importlib.import_module(module, __name__)
                                  return getattr(module, engine)
                                  except (ModuleNotFoundError, AttributeError) as ex:
                                  return mock_missing(engine, msg=str(ex))


                                  To test it:



                                  from engines import try_load

                                  PSI4 = try_load("PSI4", ".psi4")
                                  a = PSI4()

                                  # module does not exist
                                  try:
                                  PSI5 = try_load("PSI5", ".PSI5")
                                  b = PSI5()
                                  raise AssertionError("This should have failed!")
                                  except ImportError as ex:
                                  print(str(ex))

                                  # class does not exist in module
                                  try:
                                  PSI6 = try_load("PSI6", ".psi4")
                                  c = PSI6()
                                  raise AssertionError("This should have failed!")
                                  except ImportError as ex:
                                  print(str(ex))



                                  This outputs:



                                  The class PSI5 you tried to call is not importable; this is likely due to it not being installed. Original reason: No module named 'engines.psi5'
                                  The class PSI6 you tried to call is not importable; this is likely due to it not being installed. Original reason: module 'engines.psi4' has no attribute 'PSI6'





                                  share|improve this answer









                                  $endgroup$















                                    1












                                    1








                                    1





                                    $begingroup$

                                    This answer builds upon the solution presented by Mathias Ettinger




                                    If you want to get rid of the try: ... except: ... boilerplate you could implement some kind of custom loader for your engines.



                                    This is something I wrote quickly, which is likely far from perfect, but it works.



                                    in __init__.py:



                                    import importlib


                                    def mock_missing(name, msg):
                                    def init(self, *args, **kwargs):
                                    raise ImportError(
                                    f'The class name you tried to call is not importable; '
                                    f'this is likely due to it not being installed. '
                                    f'Original reason: msg')
                                    return type(name, (), '__init__': init)


                                    def try_load(engine, module):
                                    """Try to load an engine, return a mock class if it was not found

                                    Inspired by https://stackoverflow.com/a/10675081
                                    """
                                    try:
                                    module = importlib.import_module(module, __name__)
                                    return getattr(module, engine)
                                    except (ModuleNotFoundError, AttributeError) as ex:
                                    return mock_missing(engine, msg=str(ex))


                                    To test it:



                                    from engines import try_load

                                    PSI4 = try_load("PSI4", ".psi4")
                                    a = PSI4()

                                    # module does not exist
                                    try:
                                    PSI5 = try_load("PSI5", ".PSI5")
                                    b = PSI5()
                                    raise AssertionError("This should have failed!")
                                    except ImportError as ex:
                                    print(str(ex))

                                    # class does not exist in module
                                    try:
                                    PSI6 = try_load("PSI6", ".psi4")
                                    c = PSI6()
                                    raise AssertionError("This should have failed!")
                                    except ImportError as ex:
                                    print(str(ex))



                                    This outputs:



                                    The class PSI5 you tried to call is not importable; this is likely due to it not being installed. Original reason: No module named 'engines.psi5'
                                    The class PSI6 you tried to call is not importable; this is likely due to it not being installed. Original reason: module 'engines.psi4' has no attribute 'PSI6'





                                    share|improve this answer









                                    $endgroup$



                                    This answer builds upon the solution presented by Mathias Ettinger




                                    If you want to get rid of the try: ... except: ... boilerplate you could implement some kind of custom loader for your engines.



                                    This is something I wrote quickly, which is likely far from perfect, but it works.



                                    in __init__.py:



                                    import importlib


                                    def mock_missing(name, msg):
                                    def init(self, *args, **kwargs):
                                    raise ImportError(
                                    f'The class name you tried to call is not importable; '
                                    f'this is likely due to it not being installed. '
                                    f'Original reason: msg')
                                    return type(name, (), '__init__': init)


                                    def try_load(engine, module):
                                    """Try to load an engine, return a mock class if it was not found

                                    Inspired by https://stackoverflow.com/a/10675081
                                    """
                                    try:
                                    module = importlib.import_module(module, __name__)
                                    return getattr(module, engine)
                                    except (ModuleNotFoundError, AttributeError) as ex:
                                    return mock_missing(engine, msg=str(ex))


                                    To test it:



                                    from engines import try_load

                                    PSI4 = try_load("PSI4", ".psi4")
                                    a = PSI4()

                                    # module does not exist
                                    try:
                                    PSI5 = try_load("PSI5", ".PSI5")
                                    b = PSI5()
                                    raise AssertionError("This should have failed!")
                                    except ImportError as ex:
                                    print(str(ex))

                                    # class does not exist in module
                                    try:
                                    PSI6 = try_load("PSI6", ".psi4")
                                    c = PSI6()
                                    raise AssertionError("This should have failed!")
                                    except ImportError as ex:
                                    print(str(ex))



                                    This outputs:



                                    The class PSI5 you tried to call is not importable; this is likely due to it not being installed. Original reason: No module named 'engines.psi5'
                                    The class PSI6 you tried to call is not importable; this is likely due to it not being installed. Original reason: module 'engines.psi4' has no attribute 'PSI6'






                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered 7 hours ago









                                    AlexVAlexV

                                    2,8729 silver badges32 bronze badges




                                    2,8729 silver badges32 bronze badges



























                                        draft saved

                                        draft discarded
















































                                        Thanks for contributing an answer to Code Review Stack Exchange!


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid


                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.

                                        Use MathJax to format equations. MathJax reference.


                                        To learn more, see our tips on writing great answers.




                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function ()
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f222872%2fskipping-over-failed-imports-until-they-are-needed-if-ever%23new-answer', 'question_page');

                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown







                                        Popular posts from this blog

                                        Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

                                        Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

                                        Ласкавець круглолистий Зміст Опис | Поширення | Галерея | Примітки | Посилання | Навігаційне меню58171138361-22960890446Bupleurum rotundifoliumEuro+Med PlantbasePlants of the World Online — Kew ScienceGermplasm Resources Information Network (GRIN)Ласкавецькн. VI : Літери Ком — Левиправивши або дописавши її