Skip to content

Support src layout and holm app in a subpackage (even with editable installs) #41

Closed

Description

@davidbrochart

Holm seems to work fine with a flat layout, but not with an src layout.

Activity

  1. coderabbitai commented on Dec 22, 2025

    @coderabbitai
  2. volfpeter commented on Dec 22, 2025

    @volfpeter
    Owner

    Hi David,

    Thanks for giving holm a go and for reporting the issue.

    You ran into a (so far) half-handled edge case: using the src layout without an application package in it.

    I've created an example project from the actions-with-htmy example like this:

    • uv init a new project
    • Install dependencies: uv add holm uvicorn poethepoet
    • Create a src dir in project
    • Copy application files from the actions-with-htmy example directly into the src folder.
    • Set up a poethepoet start task like this: start.cmd = "uvicorn main:app --reload" and start.cwd = "src"
    • Run uv run poe start

    If I start this project, imports do fail.

    As a workaround, please create a package in src and move the application code into that package. After that, you need to update the start command like this: start.cmd = "uvicorn <app-package-name>.main:app --reload". If you run uv run poe start now, the application will start as expected.

    Image

    I'll try to fix this issue as soon as as I can, but a contribution would also be very welcome. FWIW: my current priority is to finalize async HTML streaming support in FastHX and then bring the feature to holm to optimize time to first byte and first contentful paint.

  3. davidbrochart commented on Dec 22, 2025

    @davidbrochart
    ContributorAuthor

    Thanks for helping @volfpeter.
    Unfortunately, I don't have control on how the server is run, as it is part of a more complex project. For now, I created a link from my_project to src/my_project, but that will only work in development mode. I'll try and see if I can come up with a real fix.
    Thanks for your work in the holm/htmy/FastHX stack, it's awesome.

  4. volfpeter commented on Dec 22, 2025

    @volfpeter
    Owner

    Unfortunately, I don't have control on how the server is run

    Ah, okay, I think I get the issue. Basically you need the main entry point to be specific Python module directly in src/.

    The best solution/workaround for now is this:

    • Follow the instructions from my previous comment.
    • Add a main.py file directly in src/, it should contain a single line: from app.main import app as app.
    • Change start.cmd back to "main:app".
    • Run uv run poe start.

    With this setup, you can have the main entry point directly in src/. The holm part of the application is located in the src/app (in my example, replace my_project with your example) package, but you have the ASGI app directly in src/main.py. The setup works because the app (my_project) package is on the Python search path, holm also works because the application is wrapped in a package, and you have the entry point exactly as you need it.

    (The library uses wherever you have the holm.App() call as your application's root. The application's actual entry point can be in any other place, that's out of scope for holm. In the above case I moved it to src/main.py for uvicorn. You could also wrap the Holm call a factory function and pass a pre-configured FastAPI instance to it that's created somewhere else.)

    I hope it helps. It feels better then setting up symlinks. It should work in production.


    When it comes to debugging and fixing this issue, the problems is most likely in the holm._model.PackageInfo class. The import_module() method already handles the flat (not packaged) app structure as a special case, it's likely we need special handling for the flat structure with src layout case. My other candidate is from_marker_file.

    I'll try to make some time to fix this over the coming days.


    Thanks for your work in the holm/htmy/FastHX stack, it's awesome.

    Thank you! This is very high praise. I'm happy you like these projects. If you don't mind me asking, please spread the word 🙂 The projects could really use a couple of contributors (especially documentation, which is quite lacking unfortunately).

    Not sure what you're building with this stack and at what stage it is, but I have a htmy component library for BasecoatUI in the works. The components, examples, and the example holm app are actually ready, the missing part is the documentation... I got side-tracked with the HTML streaming feature. If you need early access to the component library, get in touch and I'll add you to the project.

  5. volfpeter commented on Dec 22, 2025

    @volfpeter
    Owner

    Hi again,

    I've fixed the problem and released version 0.7.2. The fix worked in my local test project (described in my first comment), please let me know if you still get an error. Also, let me know if you have any other issues with holm/htmy/fasthx.

  6. davidbrochart commented on Dec 23, 2025

    @davidbrochart
    ContributorAuthor

    Hi @volfpeter,

    Thanks for releasing v0.7.2. I tried it but it doesn't fix the issue on my side. I don't know what you mean by "src layout without package", there is indeed a package. Isn't the issue that app_dir here:

    app_dir=root_dir / app_dir_name,

    is set to my_repo/my_package, which doesn't exist because it's in my_repo/src/my_package? But anyway, this is the development layout, which will be different when installed, so I guess that this is wrong in the first place:
    root_dir = Path.cwd()

  7. volfpeter commented on Dec 23, 2025

    @volfpeter
    Owner

    I've created 3 example applications with src layout, you can find all of them in this repo: https://github.com/volfpeter/bin (see the holm_41 dir). See the pyproject.toml for info on how I actually start them (uv run poe start).

    The src-no-app-package one failed before holm 0.7.2, now they all work.

    The one in the src-entry-point-and-app-package dir uses the init function pattern just to clearly differentiate it from the src-app-package example.

    Could you please add minimal example of your project layout and also how to start the application? I could then replicate your case exactly and try to fix it. Thanks!

  8. davidbrochart commented on Dec 23, 2025

    @davidbrochart
    ContributorAuthor

    Thanks, I opened volfpeter/bin#1.

  9. volfpeter commented on Dec 23, 2025

    @volfpeter
    Owner

    Thanks! I've merged the PR and set it up locally. I'll try to have another look at the issue today and hopefully fix it. Maybe just at night.

  10. volfpeter commented on Dec 23, 2025

    @volfpeter
    Owner

    Okay, I had a few minutes and got it working.

    I didn't want to expose the internal config that's used to discover the app, because then people could expect all kinds of nested setups to work, which could become a pain to maintain. But this setup is sensible and needs this, so it should be supported.

    The problem here is that the import root is in the application repo's root, but the application is in a subfolder. The setup only works because the app is installed in editable mode.

    The best solution I came up with so far is to add a root_dir: Path | None = None argument to both App and AppConfig.default(). If not None, it's used within AppConfig.default() instead of Path.cwd(). After these simple changes in holm, all you would need to change in your app is to pass root_dir=Path(__file__).parent.parent to App(), and everything would work.

    I've attached the holm diff (zipped, because GitHub didn't allow the diff upload...). Could you please test it when you have time? If it works, I'll create a PR and release a new version. Maybe I'll think a bit about whether there's an implementation that doesn't require manual root dir config. If you have an idea for that, please let me know 🙂

    holm-src-editable-install.zip

  11. davidbrochart commented on Dec 24, 2025

    @davidbrochart
    ContributorAuthor

    Thanks for the patch @volfpeter.
    I tried it with https://github.com/volfpeter/bin/tree/main/holm_41/src-app-package2 but it doesn't work. I called App(root_dir=Path(__file__).parent.parent), but maybe I'm missing something else?
    But even if that worked, it would break the non-editable install, right?

  12. 5 remaining items

  13. davidbrochart commented on Dec 27, 2025

    @davidbrochart
    ContributorAuthor

    It works indeed, although with a lot of ModuleNotFoundError.

  14. volfpeter commented on Dec 27, 2025

    @volfpeter
    Owner

    It works indeed

    Great! Thanks for testing the patch!

    although with a lot of ModuleNotFoundError.

    Yes, those are because holm tries to import page, layout, actions from every application package now, even if the modules don't exist. I'll restore and update the mentioned if statement, and then these logs will disappear.

  15. added a commit that references this issue on Dec 27, 2025
    9cae21f
  16. volfpeter commented on Dec 27, 2025

    @volfpeter
    Owner

    I've released version 0.7.3 with the fix. Let me know if you have any issues after the upgrade.

  17. davidbrochart commented on Dec 27, 2025

    @davidbrochart
    ContributorAuthor

    Thanks a lot, everything seems to work fine.

  18. volfpeter commented on Dec 27, 2025

    @volfpeter
    Owner

    Perfect! And thanks for reporting the issue. Have fun with holm.

  19. davidbrochart commented on Jan 2, 2026

    @davidbrochart
    ContributorAuthor

    I'm running into a similar issue, but I'm not sure I'm missing something. I have a src layout in which my holm app is in a subdirectory, for instance:

    my_package
      src
        my_package
          __init__.py
          app.py
          sub_dir
            main.py
            layout.py
            page.py
    

    I'd like to create my holm app in app.py, is it currently supported?

  20. volfpeter commented on Jan 2, 2026

    @volfpeter
    Owner

    This kind of layout (the holm app being in a subpackage) is not supported right now.

    I'm not sure how to detect the correct root in this case for imports. Maybe it's possible with deeper call stack inspection. The manual option would be to expose the AppConfig class and make it possible to pass one to the App() function. That way you could use any config object, but even then, I'm not sure all cases would be supported by the internal discovery and path creation logic (maybe we'd need to extend the AppConfig class to handle every case).

    If you want to test this approach, you can do so like this:

    • Add a config: AppConfig | None = None argument to the App() function.
    • Internally use the received AppConfig instance if there's one, otherwise create the default as it's done now.
    • Create an AppConfig instance manually that matches your project structure and pass it to App.

    In App, you should always print packages variable to see which packages the lib recognized as an application component. This is the set of PackageInfo objects that's used to build the API. I'm mentioning this print, because first we must ensure the correct packages are discovered, then we must check whether the corresponding page, layout, etc. modules can be imported as expected, and finally we can check whether the correct routes are created for them correctly.

    If it works, I'd try to implement the automatic discovery logic to handle this setup as well. I may give it a go when I have time. Maybe Claude could give it a go based on your description and the context above.

    (I assume placing the holm app package next to my_app in src is not a viable solution for you. If it is, I'd actually recommend doing that.)

  21. davidbrochart commented on Jan 2, 2026

    @davidbrochart
    ContributorAuthor

    Maybe I'm missing something, but I am wondering why the layout discovery is based on packages in the first place. Couldn't it just be based on a root directory?

  22. changed the title [-]Support src layout (even with editable installs)[/-] [+]Support src layout and holm app in a subpackage (even with editable installs)[/+] on Jan 3, 2026
  23. volfpeter commented on Jan 3, 2026

    @volfpeter
    Owner

    @davidbrochart I've opened #45 that adds support for having the holm app in a subpackage (could be deep in the package hierarchy.

    I tested it with various src and flat layouts, and also the editable install setup you've created in my "bin" repo. Everything seems to work. Could you please test whether the PR solves the issue in your local setup? If it does, I'll merge the PR and release a new patch version. Here's an example app that only works with the change in linked PR: https://github.com/volfpeter/bin/tree/main/holm_41/app-in-subpackage

  24. davidbrochart commented on Jan 3, 2026

    @davidbrochart
    ContributorAuthor

    Works like a charm, thanks so much for your help again @volfpeter!

  25. volfpeter commented on Jan 3, 2026

    @volfpeter
    Owner

    Great, thanks for testing. I've already started the release, v0.7.4 should be out momentarily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions