route.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """The route decorator and associated variables and functions."""
  2. from __future__ import annotations
  3. import re
  4. from reflex import constants
  5. def verify_route_validity(route: str) -> None:
  6. """Verify if the route is valid, and throw an error if not.
  7. Args:
  8. route: The route that need to be checked
  9. Raises:
  10. ValueError: If the route is invalid.
  11. """
  12. pattern = catchall_in_route(route)
  13. if pattern and not route.endswith(pattern):
  14. raise ValueError(f"Catch-all must be the last part of the URL: {route}")
  15. def get_route_args(route: str) -> dict[str, str]:
  16. """Get the dynamic arguments for the given route.
  17. Args:
  18. route: The route to get the arguments for.
  19. Returns:
  20. The route arguments.
  21. """
  22. args = {}
  23. def add_route_arg(match: re.Match[str], type_: str):
  24. """Add arg from regex search result.
  25. Args:
  26. match: Result of a regex search
  27. type_: The assigned type for this arg
  28. Raises:
  29. ValueError: If the route is invalid.
  30. """
  31. arg_name = match.groups()[0]
  32. if arg_name in args:
  33. raise ValueError(
  34. f"Arg name [{arg_name}] is used more than once in this URL"
  35. )
  36. args[arg_name] = type_
  37. # Regex to check for route args.
  38. check = constants.RouteRegex.ARG
  39. check_strict_catchall = constants.RouteRegex.STRICT_CATCHALL
  40. check_opt_catchall = constants.RouteRegex.OPT_CATCHALL
  41. # Iterate over the route parts and check for route args.
  42. for part in route.split("/"):
  43. match_opt = check_opt_catchall.match(part)
  44. if match_opt:
  45. add_route_arg(match_opt, constants.RouteArgType.LIST)
  46. break
  47. match_strict = check_strict_catchall.match(part)
  48. if match_strict:
  49. add_route_arg(match_strict, constants.RouteArgType.LIST)
  50. break
  51. match = check.match(part)
  52. if match:
  53. # Add the route arg to the list.
  54. add_route_arg(match, constants.RouteArgType.SINGLE)
  55. return args
  56. def catchall_in_route(route: str) -> str:
  57. """Extract the catchall part from a route.
  58. Args:
  59. route: the route from which to extract
  60. Returns:
  61. str: the catchall part of the URI
  62. """
  63. match_ = constants.RouteRegex.CATCHALL.search(route)
  64. return match_.group() if match_ else ""
  65. def catchall_prefix(route: str) -> str:
  66. """Extract the prefix part from a route that contains a catchall.
  67. Args:
  68. route: the route from which to extract
  69. Returns:
  70. str: the prefix part of the URI
  71. """
  72. pattern = catchall_in_route(route)
  73. return route.replace(pattern, "") if pattern else ""