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
  • 5,765
  • 11
  • 49
  • 86

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
  • 388
  • 3
  • 14