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;
$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.
python python-3.x object-oriented error-handling dynamic-loading
$endgroup$
add a comment |
$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.
python python-3.x object-oriented error-handling dynamic-loading
$endgroup$
add a comment |
$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.
python python-3.x object-oriented error-handling dynamic-loading
$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
python python-3.x object-oriented error-handling dynamic-loading
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
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
$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')
$endgroup$
add a comment |
$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.
$endgroup$
add a comment |
$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'
$endgroup$
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$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')
$endgroup$
add a comment |
$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')
$endgroup$
add a comment |
$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')
$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')
answered 8 hours ago
Mathias EttingerMathias Ettinger
25.7k3 gold badges34 silver badges87 bronze badges
25.7k3 gold badges34 silver badges87 bronze badges
add a comment |
add a comment |
$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.
$endgroup$
add a comment |
$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.
$endgroup$
add a comment |
$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.
$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.
answered 6 hours ago
200_success200_success
134k21 gold badges169 silver badges440 bronze badges
134k21 gold badges169 silver badges440 bronze badges
add a comment |
add a comment |
$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'
$endgroup$
add a comment |
$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'
$endgroup$
add a comment |
$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'
$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'
answered 7 hours ago
AlexVAlexV
2,8729 silver badges32 bronze badges
2,8729 silver badges32 bronze badges
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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