-1

I have the structure

main.py                  from mymodule import a; a.A()
mymodule/
mymodule/__init__.py     (empty)
mymodule/a.py                                       # works when called from main.py, fails when called alone
mymodule/b.py            class B: pass
mymodule/c.py            class C: pass
mymodule/test.py         from .a import A; A()      # failing when called alone

In a.py there is:

from .b import B
from .c import C

class A:
    def __init__(self):
        self.b = B()
        self.c = C()

if __name__ == '__main__':
    A()

Calling main.py works perfectly. It calls a.py which does from .b import B.

But calling a.py alone fails on the same from .b import B with:

ImportError: attempted relative import with no known parent package

I have already read Relative imports in Python 3 and many similar questions such as How to import the class within the same directory or sub directory? but here this question is specific on:

Why does from .b import B succeed in a.py when called from main.py, and fails when called from a.py alone, or when calling test.py?

How to be able to have a test.py in the same directory than a.py and be able to import the latter? (without sys.path.append hacks)

Basj
  • 36,818
  • 81
  • 313
  • 561
  • What do you mean by "calling a.py alone"? What exactly do you execute for that? – 9769953 Jun 01 '22 at 10:04
  • @9769953 I mean: `cd mymodule; python3 test.py; python3 a.py` – Basj Jun 01 '22 at 10:05
  • Do not execute modules as if they are a script. Modules should be imported, not run. Unless you make them an executable module, but even then, they should be "run" differently. – 9769953 Jun 01 '22 at 10:06
  • @9769953 1/2 I initially did not specifically want to make it a module, I would have preferred everything as "scripts". But I created a module because it was the only non-sys-path-hack option to import another .py file from the same directory and/or from a child directory. – Basj Jun 01 '22 at 10:09
  • @9769953 2/2 But even *in a module*, it makes sense to have a script such as `test.py` to do a demo of the features of the module, and this *in the same folder* as the module itself. – Basj Jun 01 '22 at 10:10
  • For tests (or demonstrations, if you like that), preferably make a separate folder `tests` and put `test.py` in there, for clarity. You can use tools like pytest to run that. But make sure `test.py` does not use relative imports: that doesn't demonstrate/test the normal usage of a module. – 9769953 Jun 01 '22 at 10:11
  • the way i do it if you need would be : `if len(__name__.split("."))==1:#test if you are called by a module or not` `from b import B` `else:` `from .b import B` – laenNoCode Jun 01 '22 at 10:11
  • If you make a script a module because of re-used functionality elsewhere, then write a wrapper script around that module. Don't make a single file that is both a module and a script, that simply doesn't work (as shown). – 9769953 Jun 01 '22 at 10:12
  • Some info [here in an answer of mine](https://stackoverflow.com/a/55102872/9769953) to a similar question/problem. – 9769953 Jun 01 '22 at 10:13
  • @laenNoCode That feels like bad practice. An import should be unambiguous; don't do that. And so should the purpose of a file: module or script, not both. My opinion of course; your judgment. – 9769953 Jun 01 '22 at 10:14
  • So all in all: `test.py` should just contain `from a import A` or similar. Since it has a different purpose. `a.py` and `b.py` should probably be treated as modules, not be used as scripts. – 9769953 Jun 01 '22 at 10:16
  • @9769953 i know i feels like a bad practice, and I would say it is, but the amount of files that can be either a module or a demo script are (in my case) not many and temporary. + there are plenty people who already told that doing this kind of thing is a bad practice, so i didn't feel the urge to tell so – laenNoCode Jun 01 '22 at 10:18
  • @9769953 Thank you for your insight! Can you post an answer that summarizes all your comments? It would be useful for future reference. There are other similar questions, but this one is specific (see question). `So all in all: test.py should just contain from a import A or similar. ` this does not work, it is precisely the point: `ImportError: attempted relative import with no known parent package`. Importing in Python is a bit complex :) – Basj Jun 01 '22 at 10:23
  • I don't understand what you're saying: `ImportError: attempted relative import with no known parent package` will *not* happen with `from a import A`, but instead you say "this does not work". – 9769953 Jun 01 '22 at 10:26
  • @9769953 `test.py`: `from a import A; A()`: `File "D:\import_test\mymodule\a.py", line 1, in from .b import B ImportError: attempted relative import with no known parent package` – Basj Jun 01 '22 at 10:33
  • So you fixed the import for a, but did not fix the import for b. Your error come from `from .b import B`. – 9769953 Jun 01 '22 at 10:34
  • @9769953 yes, because, ultimately, I want to be able to call `mymodule/a.py` from `main.py`, so we have no choice except having a module with `__init__.py` etc, and with `from .b import B`. If `a.py` contained `from b import B` it would work alone, but not as a module anymore. – Basj Jun 01 '22 at 10:40
  • "it would work alone, but not as a module anymore.": I can only reiterate what I said before: just as a script or a module, not both. – 9769953 Jun 01 '22 at 11:26
  • There's one mistake in my comments above: use `from mymodule.a import A` instead. I overlooked the `mymodule` part. – 9769953 Jun 01 '22 at 11:27
  • Thanks @9769953. I you can paste your comments as an answer, I'll accept it and it will be useful for future reference. – Basj Jun 01 '22 at 11:48

0 Answers0