Skip to content

Commit c23bb08

Browse files
authored
Merge pull request #6 from volfpeter/feat/private-packages
Add support for private packages
2 parents a3db636 + b6788a5 commit c23bb08

File tree

10 files changed

+71
-3
lines changed

10 files changed

+71
-3
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ At the moment, the library **does not** provide security features like automatic
8686

8787
Also, do not forget about **XSS prevention**, when rendering untrusted data with custom components!
8888

89+
## AI assistance
90+
91+
The library and all its dependencies are registered at [Context7](https://context7.com/volfpeter).
92+
93+
To get good AI assistance, all you need to do is register the Context7 MCP server in your coding tool and tell the agent to use it.
94+
95+
If you are starting a new project, you can additionally point the agent at one of the example applications in the [repository](https://github.com/volfpeter/holm). With all this context and detailed instructions of the project you want to build, it will get you started quickly.
96+
97+
Because of the similarity with Next.js and React, and the standard use of FastAPI and other dependencies, you can expect good results, both for vibe coding or inline completion.
98+
8999
## Development
90100

91101
Development setup:

docs/application-components.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,15 @@ Both formats are converted to FastAPI path parameters. For example:
6060
- File path `user/_id_/profile` becomes URL `/user/{id}/profile`.
6161
- File path `user/{user_id}/settings` becomes URL `/user/{user_id}/settings`.
6262

63-
Any layout or page within these packages can access the parameter as a FastAPI dependency by adding it to their function signature (e.g., `id: int` or `userId: str`).
63+
Any layout or page within these packages can access the parameter as a FastAPI dependency by adding it to their function signature (e.g., `id: int` or `user_id: str`).
64+
65+
## Private packages
66+
67+
You can prefix package names with one or more underscores to opt the entire package (including its subpackages) out of the automatic application component discovery.
68+
69+
For example, if you have a package named `_private` somewhere in your application folder, you can freely place valid page, layout, or API files within it or its subpackages. These modules will not be processed and included in the application.
70+
71+
Reminder: if the package name also ends with an underscore, it will be treated as a path parameter, as described [above](#path-parameters-as-package-names).
6472

6573
## Error handling
6674

docs/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@ At the moment, the library **does not** provide security features like automatic
7777

7878
Also, do not forget about **XSS prevention**, when rendering untrusted data with custom components!
7979

80+
## AI assistance
81+
82+
The library and all its dependencies are registered at [Context7](https://context7.com/volfpeter).
83+
84+
To get good AI assistance, all you need to do is register the Context7 MCP server in your coding tool and tell the agent to use it.
85+
86+
If you are starting a new project, you can additionally point the agent at one of the example applications in the [repository](https://github.com/volfpeter/holm). With all this context and detailed instructions of the project you want to build, it will get you started quickly.
87+
88+
Because of the similarity with Next.js and React, and the standard use of FastAPI and other dependencies, you can expect good results, both for vibe coding or inline completion.
89+
8090
## Development
8191

8292
Development setup:

holm/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.2.0"
1+
__version__ = "0.3.0"
22

33
from .app import App as App
44
from .fastapi import FastAPIDependency as FastAPIDependency

holm/app.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import inspect
22
from collections.abc import Awaitable, Iterable
3+
from functools import lru_cache
4+
from pathlib import Path
35
from typing import cast
46

57
from fastapi import APIRouter, Depends, FastAPI
@@ -131,10 +133,22 @@ def _discover_app_packages(config: AppConfig) -> set[PackageInfo]:
131133
"""
132134
Discovers all packages that are part of the application and returns them as a set.
133135
"""
136+
137+
@lru_cache()
138+
def is_excluded(path: Path) -> bool:
139+
"""Returns whether the given file or package path should be excluded from the application."""
140+
rel_path = path.relative_to(config.root_dir)
141+
return any(
142+
# Exclude if a path segment starts with an underscore but does not end with one.
143+
# Path segments that both start and end with an underscore represent path parameters!
144+
p.startswith("_") and not p.endswith("_")
145+
for p in rel_path.parts
146+
)
147+
134148
return {
135149
PackageInfo.from_marker_file(f, config=config)
136150
for f in config.app_dir.rglob("*.py")
137-
if f.stem in module_names
151+
if f.stem in module_names and not is_excluded(f.parent) # Pass the parent to make use of caching
138152
}
139153

140154

test_app/_not_in_app/__init__.py

Whitespace-only changes.

test_app/_not_in_app/inner/__init__.py

Whitespace-only changes.

test_app/_not_in_app/inner/page.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def page() -> str:
2+
return (
3+
"This must not be included in the app because it's in an excluded package: "
4+
"there's a parent package whose name starts with an _ and does not end with _ "
5+
"(so no path parameter)."
6+
)

test_app/_not_in_app/page.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def page() -> str:
2+
return (
3+
"This must not be included in the app because it's in an excluded package: "
4+
"there's a parent package whose name starts with an _ and does not end with _ "
5+
"(so no path parameter)."
6+
)

tests/test_private_package.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import pytest
2+
from fastapi.testclient import TestClient
3+
4+
5+
@pytest.mark.parametrize(
6+
("path",),
7+
(
8+
("/_not_in_app",),
9+
("/_not_in_app/inner",),
10+
),
11+
)
12+
def test_page(client: TestClient, path: str) -> None:
13+
response = client.get(path)
14+
assert response.status_code == 404

0 commit comments

Comments
 (0)