Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions pynecone/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def add_page(
self,
component: Union[Component, ComponentCallable],
path: Optional[str] = None,
route: Optional[str] = None,
title: str = constants.DEFAULT_TITLE,
description: str = constants.DEFAULT_DESCRIPTION,
image=constants.DEFAULT_IMAGE,
Expand All @@ -172,22 +173,30 @@ def add_page(

Args:
component: The component to display at the page.
path: The path to display the component at.
path: (deprecated) The path to the component.
route: The route to display the component at.
title: The title of the page.
description: The description of the page.
image: The image to display on the page.
"""
# If the path is not set, get it from the callable.
if path is None:
if path is not None:
utils.deprecate(
"The `path` argument is deprecated for `add_page`. Use `route` instead."
)
route = path

# If the route is not set, get it from the callable.
if route is None:
assert isinstance(
component, Callable
), "Path must be set if component is not a callable."
path = component.__name__
), "Route must be set if component is not a callable."
route = component.__name__

# Check if the path given is valid
utils.verify_path_validity(path)
# Check if the route given is valid
utils.verify_route_validity(route)

self.state.setup_dynamic_args(utils.get_path_args(path))
# Apply dynamic args to the route.
self.state.setup_dynamic_args(utils.get_route_args(route))

# Generate the component if it is a callable.
component = component if isinstance(component, Component) else component()
Expand All @@ -198,7 +207,8 @@ def add_page(
)

# Format the route.
route = utils.format_route(path)
route = utils.format_route(route)

# Add the page.
self._check_routes_conflict(route)
self.pages[route] = component
Expand All @@ -209,7 +219,7 @@ def _check_routes_conflict(self, new_route: str):
Based on conflicts that NextJS would throw if not intercepted.

Raises:
ValueError: exception showing which conflict exist with the path to be added
ValueError: exception showing which conflict exist with the route to be added

Args:
new_route: the route being newly added.
Expand All @@ -233,7 +243,7 @@ def _check_routes_conflict(self, new_route: str):
and utils.catchall_prefix(route) == utils.catchall_prefix(new_route)
):
raise ValueError(
f"You cannot use multiple catchall for the same dynamic path ({route} !== {new_route})"
f"You cannot use multiple catchall for the same dynamic route ({route} !== {new_route})"
)

def add_custom_404_page(self, component, title=None, image=None, description=None):
Expand Down Expand Up @@ -297,9 +307,9 @@ def compile(self, force_compile: bool = False):

# Compile the pages.
custom_components = set()
for path, component in self.pages.items():
for route, component in self.pages.items():
component.add_style(self.style)
compiler.compile_page(path, component, self.state)
compiler.compile_page(route, component, self.state)

# Add the custom components from the page to the set.
custom_components |= component.get_custom_components()
Expand Down
6 changes: 3 additions & 3 deletions pynecone/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ def get_url(self) -> str:
return url


class PathArgType(SimpleNamespace):
"""Type of pathArg extracted from URI path."""
class RouteArgType(SimpleNamespace):
"""Type of dynamic route arg extracted from URI route."""

# Typecast to str is needed for Enum to work.
SINGLE = str("arg_single")
Expand All @@ -209,7 +209,7 @@ class RouteVar(SimpleNamespace):


class RouteRegex(SimpleNamespace):
"""Regex used for extracting path args in path."""
"""Regex used for extracting route args in route."""

ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
# group return the catchall pattern (i.e "[[..slug]]")
Expand Down
5 changes: 2 additions & 3 deletions pynecone/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,9 @@ def inner_func(self) -> List:
return inner_func

for param, value in args.items():

if value == constants.PathArgType.SINGLE:
if value == constants.RouteArgType.SINGLE:
func = argsingle_factory(param)
elif value == constants.PathArgType.LIST:
elif value == constants.RouteArgType.LIST:
func = arglist_factory(param)
else:
continue
Expand Down
57 changes: 33 additions & 24 deletions pynecone/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
StateVar = Union[PrimitiveType, Base, None]


def deprecate(msg: str):
"""Print a deprecation warning.

Args:
msg: The deprecation message.
"""
console.print(f"[yellow]DeprecationWarning: {msg}[/yellow]")


def get_args(alias: _GenericAlias) -> Tuple[Type, ...]:
"""Get the arguments of a type alias.

Expand Down Expand Up @@ -769,69 +778,69 @@ def indent(text: str, indent_level: int = 2) -> str:
return os.linesep.join(f"{' ' * indent_level}{line}" for line in lines) + os.linesep


def verify_path_validity(path: str) -> None:
"""Verify if the path is valid, and throw an error if not.
def verify_route_validity(route: str) -> None:
"""Verify if the route is valid, and throw an error if not.

Args:
path: the path that need to be checked
route: The route that need to be checked

Raises:
ValueError: explains what is wrong with the path.
ValueError: If the route is invalid.
"""
pattern = catchall_in_route(path)
if pattern and not path.endswith(pattern):
raise ValueError(f"Catch-all must be the last part of the URL: {path}")
pattern = catchall_in_route(route)
if pattern and not route.endswith(pattern):
raise ValueError(f"Catch-all must be the last part of the URL: {route}")


def get_path_args(path: str) -> Dict[str, str]:
"""Get the path arguments for the given path.
def get_route_args(route: str) -> Dict[str, str]:
"""Get the dynamic arguments for the given route.

Args:
path: The path to get the arguments for.
route: The route to get the arguments for.

Returns:
The path arguments.
The route arguments.
"""
args = {}

def add_path_arg(match: re.Match[str], type_: str):
def add_route_arg(match: re.Match[str], type_: str):
"""Add arg from regex search result.

Args:
match: result of a regex search
type_: the assigned type for this arg
match: Result of a regex search
type_: The assigned type for this arg

Raises:
ValueError: explains what is wrong with the path.
ValueError: If the route is invalid.
"""
arg_name = match.groups()[0]
if arg_name in args:
raise ValueError(
f"arg name [{arg_name}] is used more than once in this URL"
f"Arg name [{arg_name}] is used more than once in this URL"
)
args[arg_name] = type_

# Regex to check for path args.
# Regex to check for route args.
check = constants.RouteRegex.ARG
check_strict_catchall = constants.RouteRegex.STRICT_CATCHALL
check_opt_catchall = constants.RouteRegex.OPT_CATCHALL

# Iterate over the path parts and check for path args.
args = {}
for part in path.split("/"):
# Iterate over the route parts and check for route args.
for part in route.split("/"):
match_opt = check_opt_catchall.match(part)
if match_opt:
add_path_arg(match_opt, constants.PathArgType.LIST)
add_route_arg(match_opt, constants.RouteArgType.LIST)
break

match_strict = check_strict_catchall.match(part)
if match_strict:
add_path_arg(match_strict, constants.PathArgType.LIST)
add_route_arg(match_strict, constants.RouteArgType.LIST)
break

match = check.match(part)
if match:
# Add the path arg to the list.
add_path_arg(match, constants.PathArgType.SINGLE)
# Add the route arg to the list.
add_route_arg(match, constants.RouteArgType.SINGLE)
return args


Expand Down
4 changes: 2 additions & 2 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def test_add_page_set_route(app: App, index_page):
index_page: The index page.
"""
assert app.pages == {}
app.add_page(index_page, path="/test")
app.add_page(index_page, route="/test")
assert set(app.pages.keys()) == {"test"}


Expand All @@ -107,7 +107,7 @@ def test_add_page_set_route_nested(app: App, index_page):
index_page: The index page.
"""
assert app.pages == {}
app.add_page(index_page, path="/test/nested")
app.add_page(index_page, route="/test/nested")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling for this breaking change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a deprecation warning

assert set(app.pages.keys()) == {"test/nested"}


Expand Down