Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import module order issue #651

Closed
venu opened this issue Feb 12, 2024 · 5 comments
Closed

Import module order issue #651

venu opened this issue Feb 12, 2024 · 5 comments

Comments

@venu
Copy link
Contributor

venu commented Feb 12, 2024

  • aiohttp-devtools version:
    1.1.2
  • aiohttp version:
    3.9.3
  • python version:
    3.11
  • Platform:
    linux

Issue Summary

If you have a package with same module name, adev tries to import the module from that package rather than honoring the project root.

Assume project structure is like this

- lib
  - l1
    - app.py
- services
  - s1
   - app.py

sample command:
adev runserver app.py --root=/opt/app/services/s1/ --app-factory=create_app --port=8080 --aux-port=8086 --verbose --no-livereload

I am assuming, it will load the app modules from s1 root. But its loading app module from l1 package which has app module.

I debugged the issue and found that we are appending the root as module path. Python is seraching the module in sequential order and load the app module from l1 package as its in sys path ahead of root

sys.path.append(str(self.python_path))

I changed to following sys.path.insert(0, str(self.python_path)) and it sovled the issue!

@Dreamsorcerer
Copy link
Member

I think that's a bit of an unusual case, but if you can write a test to reproduce that, then it seems like a reasonable change.

@stalkerg
Copy link

@Dreamsorcerer names like app.py or main.py is very common and can conflicting between lib and application. It's have sense if package can be use as lib or as application.

@venu
Copy link
Contributor Author

venu commented Feb 13, 2024

Here are the simple steps to reproduce

Folder Structure

libs
  - l1
    - `__init__.py`
    - app.py
app.py

app.py

from aiohttp import web

import sys
sys.path.insert(0, 'libs/l1')

class Main:
    def __init__(self):
        self.app = web.Application()
        self.app.router.add_get('/', self.handle_request)

    async def handle_request(self, request):
        return web.Response(text="testing", content_type='text/html')

def create_app():
    page = Main()
    return page.app

libs/l1/app.py

print("I am app")

Test
adev runserver app.py --port=8080 --aux-port=8086 --verbose --no-livereload

[15:52:30] Root path not specified, using current working directory
[15:52:30] app_path is a file, returning it directly
[15:52:30] config loaded:
Config:
  py_file: PosixPath('*********/test_devtools/app.py')
  static_path: None
  static_url: '/static/'
  livereload: False
  shutdown_by_url: False
  path_prefix: '/_devtools'
  app_factory_name: None
  host: 'localhost'
  main_port: 8080
  aux_port: 8086
[15:52:30] successfully loaded "app" from "*********/test_devtools"
[15:52:30] found default attribute "create_app" in module "<module 'app' from '*********/test_devtools/app.py'>"
[15:52:30] Starting aux server at http://localhost:8086 ◆
[15:52:30] Starting dev server at http://localhost:8080 ●
======== Running on http://0.0.0.0:8086 ========
(Press CTRL+C to quit)
I am app
[15:52:30] successfully loaded "app" from "*********/test_devtools"
Process Process-1:
Traceback (most recent call last):
  File "*********/test_devtools/venv/lib/python3.11/site-packages/aiohttp_devtools/runserver/config.py", line 160, in import_app_factory
    attr = getattr(module, self.app_factory_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'app' has no attribute 'create_app'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.11/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/lib/python3.11/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "*********/test_devtools/venv/lib/python3.11/site-packages/aiohttp_devtools/runserver/serve.py", line 168, in serve_main_app
    app_factory = config.import_app_factory()
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*********/test_devtools/venv/lib/python3.11/site-packages/aiohttp_devtools/runserver/config.py", line 162, in import_app_factory
    raise AdevConfigError("Module '{}' does not define a '{}' attribute/class".format(
aiohttp_devtools.exceptions.AiohttpDevConfigError: Module 'app.py' does not define a 'create_app' attribute/class

@Dreamsorcerer
Copy link
Member

Here are the simple steps to reproduce

I meant a test, in a PR.

@venu
Copy link
Contributor Author

venu commented Feb 13, 2024

Here is the reproducable test, seems its happening only with process.. so can't mock process in this test.

# test_runserver_main.py

from multiprocessing import set_start_method
from aiohttp_devtools.runserver.watch import AppTask

@forked
def test_start_runserver_with_multi_app_modules(tmpworkdir, event_loop, capfd):
    mktree(tmpworkdir, {
        'app.py': f"""\
from aiohttp import web
import sys
sys.path.insert(0, '{tmpworkdir}/libs/l1')

async def hello(request):
    return web.Response(text='<h1>hello world</h1>', content_type='text/html')

async def create_app():
    a = web.Application()
    a.router.add_get('/', hello)
    return a
""",
        "libs": {
            "l1": {
                "__init__.py": "",
                "app.py": "print('wrong_import')"
            }
        }
    })

    set_start_method('spawn')
    config = Config(app_path="app.py", root_path=tmpworkdir, main_port=0, app_factory_name="create_app")
    config.import_app_factory()
    app_task = AppTask(config)
    
    app_task._start_dev_server()
    app_task._process.join(2)

    captured = capfd.readouterr()
    assert captured.out == "wrong_import\n"

Here is the working test..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants