0

In a certain programme I have an abstract class that should be implemented in two different sub-packages for different purposes. A simplified version of this structure is:

programme/
  __init__.py
  Abstract.py
  pkgA/
    __init__.py
    ClassA.py
  pkgB/
    __init__.py
    ClassB.py

Up to Python 3.2 (I believe), it was possible to import the Abstract class within the a subfolder with a relative reference:

from .. import Abstract

Which with Python 3.6 devolves the error message:

ValueError: attempted relative import beyond top-level package

The classical (and ugly) alternative is to add the parent folder to the path at run time:

import sys
import os

sys.path.append(os.getcwd() + '/..')
from programme import Abstract

But this also fails:

ModuleNotFoundError: No module named 'programme'

How is this now done with Python 3.6? Preferably without modifying the path at run time.

Luís de Sousa
  • 4,634
  • 7
  • 44
  • 77
  • Please re-read the answer you linked: https://stackoverflow.com/a/9331002/2066215 It doesn't mention `parent` anywhere, nor is there a file or folder called `parent` in your directory structure, so there is `No module named 'parent'`, but you can `import Abstract` – ForceBru Jan 23 '20 at 15:12
  • Does this answer your question? [Relative imports for the billionth time](https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time) – Chris Jan 23 '20 at 15:20
  • @ForceBru, now corrected. – Luís de Sousa Jan 23 '20 at 16:20
  • @Chris That question is on Python 2.7. The mechanisms for that version no longer apply to more recent versions. – Luís de Sousa Jan 23 '20 at 16:23
  • Okay, then [Relative imports in Python 3](https://stackoverflow.com/q/16981921/354577) – Chris Jan 23 '20 at 16:47

2 Answers2

2

You can use a __init__.py at the top level, programme/__init__.py, then import in the sub-module from programme.

Example:

# programme/__init__.py
from .Abstrac import Abstract
# programme/Abstract.py
class Abstract:
    def __init__(self):
        print('Abstract')
# programme/pkgA/ClassA.py
from programme import Abstract

class ClassA(Abstract):
    def __init__(self):
        super().__init__()

if __name__ == '__main__':
    a = ClassA()

Note, if you running as a script you should do:

$ cd programme
$ python -m programme.classA.ClassA
Abstract
AdielM
  • 31
  • 3
  • This solution does not work. Running as you suggest returns the error: `Error while finding module specification for 'programme.classA.ClassA' (ModuleNotFoundError: No module named 'programme')`. Running `classA.py` from the `pkgA` folder returns: `ModuleNotFoundError: No module named 'programme'`. – Luís de Sousa Jan 27 '20 at 15:51
1

You need to a level higher than programme.

This needs to be added to the code:

import os, sys
current_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
sys.path.append(os.path.normpath(os.path.join(current_dir, '..', '..')))
from programme import Abstract

This has worked when doing python3.7 ClassA.py on programme/pkgA and it was picked from this response: Ultimate answer to relative python imports

Jorge Mendes
  • 309
  • 2
  • 14