Search code examples
pythonhug

Python relative imports behavior


I have simplified my problem into this:

├── demo
│   ├── mypkg
│   │   ├── a.py
│   │   ├── b.py
│   │   └── api.py
│   │   └── startserver.py
│   └── readme.md

a.py has =>

from b import name

def greeting():
    return name()

b.py has =>

def name():
    return 'Joe'

api.py has =>

import hug
from a import greeting
@hug.get('/ping')
def ping():
    return {"response": "pong"}

@hug.get('/hello')
def hello():
    #name = hello()
    return {"response": greeting()}

startserver.py has =>

import subprocess

with open('testapi.log', 'w') as fd:
    subprocess.run(['hug', '-f', 'api.py'], stdout=fd , stderr=subprocess.STDOUT, bufsize=0)

Using .venv in VSCode terminal from \demo\mypkg> on windows 10 either of the following two commands work great, so that browser can see both /ping and /hello results just fine:

\demo\mypkg> hug -f api.py
\demo\mypkg> python startserver.py

I want to be able to run this from outside mypkg, so to the folder "mypkg" I added init & main also changed to relative imports, now I have:

├── demo
│   ├── mypkg
│   │   ├── __init__.py
│   │   ├── __main__.py
│   │   ├── a.py
│   │   ├── b.py
│   │   └── api.py
│   │   └── startserver.py
│   └── readme.md

__init__.py has =>

print('inside __init__.py')

__all__ = [
    "a",      
    "b",  
    "api",
    "startserver"
]
from . import *

__main__.py has =>

print('inside __main__.py')
import traceback
from .startserver import start

def main():
    try:
        start()
    except Exception:
        print(traceback.format_exc())

if __name__ == "__main__":
    print('... inside name == main ...')
    main()

a.py has =>

from .b import name

def greeting():
    return name()

b.py has =>

def name():
    return 'Joe'

api.py has =>

import hug
from .a import greeting
@hug.get('/ping')
def ping():
    return {"response": "pong"}

@hug.get('/hello')
def hello():
    #name = hello()
    return {"response": greeting()}

startserver.py has =>

import subprocess
import traceback
import os
from pathlib import Path

def start():
    
  try:
    currentpath = Path(__file__)
    print(f'Currently executing from {currentpath}')
    apipath = os.path.join(currentpath.parent, 'api.py')
    print(f'parse api path is {apipath}')
    print('inside startserver start()')
    with open('testapi.log', 'w') as fd:
        subprocess.run(['hug', '-f', apipath], stdout=fd , stderr=subprocess.STDOUT, bufsize=0)
  except Exception:
     print(traceback.format_exc())

then when called with "python -m mypkg" I get this error:

File "...\demo\mypkg\api.py", line 2, in <module>
    from .a import greeting
ImportError: attempted relative import with no known parent package

Reading lots of similar questions here, I figure its probably because of being called directly as a script. Issue is, how can I call the hug api inside a subprocess from outside the package? I need to be able to run this thru pyinstaller to produce a .exe Any help is greatly appreciated, thanks!


Solution

  • From nigh_anxiety link, doing this worked for me:

    try:
        from .b import name
    except ImportError:
        from b import name