2

I often write self-test code at the bottom of a module, i.e.

if __name__ == '__main__':

.
.
.

I want to keep this in the module so that if I modify it I can still run self-test on it. The module is part of a package. So there are inter-package references that need to be resolved; but these are resolved differently if I'm importing the package versus running the module standalone.

I end up with a kludge like this at the top of my modules, which is certainly ugly and probably not 'pythonic':

if __name__ == '__main__':

    from CovSample import CovSample
    from ArrayByRow import ArrayByRow    
else:

    from CEOpt import CovSample
    from CEOpt import ArrayByRow

This works - if I'm importing package CEOpt - the else-branch references work, and if I'm running standalone - the direct module name imports work. But it's not pretty I would like one import statement for the inter-package calls that still work in standalone module test. Is that possible?

Keith Brodie
  • 657
  • 3
  • 17
  • Note that a nicer way to do *within*-package imports is to do them relatively (i.e. without having to name `CEOpt` explicitly) and make that relativity explicit using prefixed dots: `from .CovSample import CovSample` and `from .ArrayByRow import ArrayByRow`. However, that doesn't work when `__name__=='__main__'` either, so it doesn't answer your question. I too would be very interested to hear if there was a universal way. – jez Nov 09 '19 at 23:41

1 Answers1

0

It seems that there's no universal way that will work in both cases, __name__=='__main__' and __name__!='__main__'.

What I do is the following: define a Test() function inside each submodule (so CEOpt.CovSample.Test() and CEOpt.ArrayByRow.Test()). Then create a single CEOpt/__main__.py file, which is the only file from the CEOpt package that you will ever "run" directly (that particular filename ensures that it is the one that gets run when you say python -m CEOpt from your shell). This file must import CEOpt explicitly by name, but at least now that only has to happen in one place (the other files can use relative imports with prefixed dot syntax—for example from .CovArray import CovArray). Now program the logic of __main__.py such that it responds to subcommands passed in via sys.argv, selecting which submodule's Test() function to run.

So for example, the syntax python -m CEOpt test CovArray (or equivalently %run CEOpt/__main__ test CovArray from an IPython prompt) would trigger __main__.py to call CEOpt.CovArray.Test()

jez
  • 14,867
  • 5
  • 37
  • 64
  • thanks - that's a good approach. In my case - I usually develop a module then later decide I want it to live in a package. So I have a period of weeks or months where the module must run without the package overhead - and then I drop it in the package. Your method would work for me if I knew I was making a package from the start. I may have to live with my kludge or start planning my work! – Keith Brodie Nov 09 '19 at 23:56
  • Maybe you could still put the test code in `Test()`, from the beginning, but get into the habit of saying `import thing; thing.Test()` instead of `run thing`. Or you could create your own IPython magic `%runtest` that performs `importlib.reload(importlib.import_module(targetModuleName)).Test()` on a named target (sub-)module. Then you only need to type `%runtest CovArray`—or, later in your development, `%runtest CEOpt.CovArray` – jez Nov 10 '19 at 00:22