clear-skies 2.0.6__py3-none-any.whl → 2.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clear-skies might be problematic. Click here for more details.

Files changed (149) hide show
  1. {clear_skies-2.0.6.dist-info → clear_skies-2.0.8.dist-info}/METADATA +1 -1
  2. clear_skies-2.0.8.dist-info/RECORD +252 -0
  3. clearskies/__init__.py +2 -2
  4. clearskies/authentication/authentication.py +1 -3
  5. clearskies/authentication/authorization.py +12 -5
  6. clearskies/authentication/authorization_pass_through.py +5 -3
  7. clearskies/authentication/jwks.py +25 -23
  8. clearskies/authentication/secret_bearer.py +15 -17
  9. clearskies/autodoc/schema/schema.py +1 -1
  10. clearskies/backends/api_backend.py +50 -56
  11. clearskies/backends/backend.py +14 -14
  12. clearskies/backends/cursor_backend.py +17 -23
  13. clearskies/backends/memory_backend.py +27 -30
  14. clearskies/backends/secrets_backend.py +13 -18
  15. clearskies/column.py +44 -56
  16. clearskies/columns/audit.py +14 -13
  17. clearskies/columns/belongs_to_id.py +10 -15
  18. clearskies/columns/belongs_to_model.py +6 -9
  19. clearskies/columns/belongs_to_self.py +13 -9
  20. clearskies/columns/boolean.py +13 -16
  21. clearskies/columns/category_tree.py +9 -11
  22. clearskies/columns/category_tree_children.py +2 -3
  23. clearskies/columns/category_tree_descendants.py +1 -1
  24. clearskies/columns/created.py +8 -11
  25. clearskies/columns/created_by_authorization_data.py +7 -9
  26. clearskies/columns/created_by_header.py +12 -8
  27. clearskies/columns/created_by_ip.py +6 -8
  28. clearskies/columns/created_by_routing_data.py +12 -7
  29. clearskies/columns/created_by_user_agent.py +6 -9
  30. clearskies/columns/date.py +12 -14
  31. clearskies/columns/datetime.py +19 -17
  32. clearskies/columns/email.py +3 -1
  33. clearskies/columns/float.py +10 -14
  34. clearskies/columns/has_many.py +8 -10
  35. clearskies/columns/has_many_self.py +13 -7
  36. clearskies/columns/has_one.py +2 -0
  37. clearskies/columns/integer.py +9 -11
  38. clearskies/columns/json.py +10 -12
  39. clearskies/columns/many_to_many_ids.py +14 -16
  40. clearskies/columns/many_to_many_ids_with_data.py +16 -16
  41. clearskies/columns/many_to_many_models.py +5 -7
  42. clearskies/columns/many_to_many_pivots.py +3 -5
  43. clearskies/columns/phone.py +12 -9
  44. clearskies/columns/select.py +12 -9
  45. clearskies/columns/string.py +1 -1
  46. clearskies/columns/timestamp.py +15 -15
  47. clearskies/columns/updated.py +8 -10
  48. clearskies/columns/uuid.py +7 -10
  49. clearskies/configs/any.py +2 -0
  50. clearskies/configs/any_dict.py +2 -0
  51. clearskies/configs/any_dict_or_callable.py +2 -0
  52. clearskies/configs/boolean.py +2 -0
  53. clearskies/configs/boolean_or_callable.py +2 -0
  54. clearskies/configs/callable_config.py +2 -0
  55. clearskies/configs/config.py +2 -0
  56. clearskies/configs/datetime.py +2 -0
  57. clearskies/configs/datetime_or_callable.py +2 -0
  58. clearskies/configs/float.py +2 -0
  59. clearskies/configs/float_or_callable.py +2 -0
  60. clearskies/configs/integer.py +2 -0
  61. clearskies/configs/integer_or_callable.py +2 -0
  62. clearskies/configs/list_any_dict.py +2 -0
  63. clearskies/configs/list_any_dict_or_callable.py +2 -0
  64. clearskies/configs/model_column.py +2 -0
  65. clearskies/configs/model_columns.py +2 -0
  66. clearskies/configs/model_destination_name.py +2 -1
  67. clearskies/configs/model_to_id_column.py +2 -0
  68. clearskies/configs/readable_model_column.py +2 -0
  69. clearskies/configs/readable_model_columns.py +2 -0
  70. clearskies/configs/searchable_model_columns.py +2 -0
  71. clearskies/configs/select.py +2 -0
  72. clearskies/configs/select_list.py +2 -0
  73. clearskies/configs/string.py +2 -0
  74. clearskies/configs/string_dict.py +2 -0
  75. clearskies/configs/string_list.py +2 -0
  76. clearskies/configs/string_list_or_callable.py +2 -0
  77. clearskies/configs/timedelta.py +2 -0
  78. clearskies/configs/timezone.py +2 -0
  79. clearskies/configs/url.py +2 -0
  80. clearskies/configs/writeable_model_column.py +2 -0
  81. clearskies/configs/writeable_model_columns.py +2 -0
  82. clearskies/configurable.py +2 -0
  83. clearskies/contexts/cli.py +9 -1
  84. clearskies/contexts/context.py +13 -14
  85. clearskies/contexts/wsgi.py +12 -10
  86. clearskies/contexts/wsgi_ref.py +12 -6
  87. clearskies/decorators.py +1 -1
  88. clearskies/decorators.pyi +10 -0
  89. clearskies/di/__init__.py +2 -1
  90. clearskies/di/di.py +7 -6
  91. clearskies/di/inject/by_class.py +2 -0
  92. clearskies/di/inject/by_name.py +2 -0
  93. clearskies/di/inject/di.py +2 -0
  94. clearskies/di/inject/environment.py +1 -1
  95. clearskies/di/inject/now.py +2 -0
  96. clearskies/di/inject/requests.py +2 -0
  97. clearskies/di/inject/secrets.py +2 -2
  98. clearskies/di/inject/utcnow.py +2 -0
  99. clearskies/di/inject/uuid.py +2 -2
  100. clearskies/end.py +45 -7
  101. clearskies/endpoint.py +43 -59
  102. clearskies/endpoint_group.py +15 -18
  103. clearskies/endpoints/advanced_search.py +19 -26
  104. clearskies/endpoints/callable.py +10 -16
  105. clearskies/endpoints/create.py +6 -10
  106. clearskies/endpoints/delete.py +5 -11
  107. clearskies/endpoints/get.py +11 -15
  108. clearskies/endpoints/health_check.py +9 -11
  109. clearskies/endpoints/list.py +29 -36
  110. clearskies/endpoints/restful_api.py +43 -53
  111. clearskies/endpoints/schema.py +14 -18
  112. clearskies/endpoints/simple_search.py +5 -12
  113. clearskies/endpoints/update.py +6 -11
  114. clearskies/environment.py +2 -0
  115. clearskies/input_outputs/cli.py +2 -0
  116. clearskies/input_outputs/headers.py +2 -0
  117. clearskies/input_outputs/input_output.py +15 -15
  118. clearskies/input_outputs/programmatic.py +2 -2
  119. clearskies/input_outputs/wsgi.py +2 -2
  120. clearskies/model.py +120 -25
  121. clearskies/query/query.py +1 -4
  122. clearskies/secrets/__init__.py +2 -1
  123. clearskies/secrets/akeyless.py +12 -10
  124. clearskies/secrets/secrets.py +7 -2
  125. clearskies/security_header.py +4 -2
  126. clearskies/security_headers/cache_control.py +15 -14
  127. clearskies/security_headers/cors.py +10 -9
  128. clearskies/security_headers/csp.py +25 -24
  129. clearskies/security_headers/hsts.py +6 -5
  130. clearskies/typing.py +1 -1
  131. clearskies/validator.py +5 -6
  132. clearskies/validators/after_column.py +6 -7
  133. clearskies/validators/before_column.py +2 -0
  134. clearskies/validators/in_the_future.py +5 -8
  135. clearskies/validators/in_the_future_at_least.py +2 -0
  136. clearskies/validators/in_the_future_at_most.py +2 -0
  137. clearskies/validators/in_the_past.py +5 -8
  138. clearskies/validators/in_the_past_at_least.py +2 -0
  139. clearskies/validators/in_the_past_at_most.py +2 -0
  140. clearskies/validators/maximum_length.py +4 -5
  141. clearskies/validators/maximum_value.py +4 -4
  142. clearskies/validators/minimum_length.py +4 -4
  143. clearskies/validators/minimum_value.py +4 -4
  144. clearskies/validators/required.py +2 -4
  145. clearskies/validators/timedelta.py +8 -9
  146. clearskies/validators/unique.py +2 -3
  147. clear_skies-2.0.6.dist-info/RECORD +0 -251
  148. {clear_skies-2.0.6.dist-info → clear_skies-2.0.8.dist-info}/WHEEL +0 -0
  149. {clear_skies-2.0.6.dist-info → clear_skies-2.0.8.dist-info}/licenses/LICENSE +0 -0
@@ -5,13 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable
5
5
 
6
6
  import requests
7
7
 
8
- import clearskies.columns.datetime
9
- import clearskies.columns.json
10
- import clearskies.configs
11
- import clearskies.configurable
12
- import clearskies.decorators
13
- import clearskies.model
14
- import clearskies.query
8
+ from clearskies import columns, configs, configurable, decorators
15
9
  from clearskies.autodoc.schema import Integer as AutoDocInteger
16
10
  from clearskies.autodoc.schema import Schema as AutoDocSchema
17
11
  from clearskies.autodoc.schema import String as AutoDocString
@@ -20,10 +14,12 @@ from clearskies.di import InjectableProperties, inject
20
14
  from clearskies.functional import routing, string
21
15
 
22
16
  if TYPE_CHECKING:
23
- import clearskies.column
17
+ from clearskies import Column, Model
18
+ from clearskies.authentication import Authentication
19
+ from clearskies.query import Query
24
20
 
25
21
 
26
- class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProperties):
22
+ class ApiBackend(configurable.Configurable, Backend, InjectableProperties):
27
23
  """
28
24
  Fetch and store data from an API endpoint.
29
25
 
@@ -314,14 +310,14 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
314
310
 
315
311
  Note: this is treated as a 'folder' path: if set, it becomes the URL prefix and is followed with a '/'
316
312
  """
317
- base_url = clearskies.configs.String(default="")
313
+ base_url = configs.String(default="")
318
314
 
319
315
  """
320
316
  A suffix to append to the end of the URL.
321
317
 
322
318
  Note: this is treated as a 'folder' path: if set, it becomes the URL suffix and is prefixed with a '/'
323
319
  """
324
- url_suffix = clearskies.configs.String(default="")
320
+ url_suffix = configs.String(default="")
325
321
 
326
322
  """
327
323
  An instance of clearskies.authentication.Authentication that handles authentication to the API.
@@ -392,12 +388,12 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
392
388
 
393
389
  ```
394
390
  """
395
- authentication = clearskies.configs.Authentication(default=None)
391
+ authentication = configs.Authentication(default=None)
396
392
 
397
393
  """
398
394
  A dictionary of headers to attach to all outgoing API requests
399
395
  """
400
- headers = clearskies.configs.StringDict(default={})
396
+ headers = configs.StringDict(default={})
401
397
 
402
398
  """
403
399
  The casing used in the model (snake_case, camelCase, TitleCase)
@@ -478,14 +474,14 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
478
474
  }
479
475
  ```
480
476
  """
481
- model_casing = clearskies.configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
477
+ model_casing = configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
482
478
 
483
479
  """
484
480
  The casing used by the API response (snake_case, camelCase, TitleCase)
485
481
 
486
482
  See model_casing for details and usage.
487
483
  """
488
- api_casing = clearskies.configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
484
+ api_casing = configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
489
485
 
490
486
  """
491
487
  A mapping from the data keys returned by the API to the data keys expected in the model
@@ -554,24 +550,24 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
554
550
  }
555
551
  ```
556
552
  """
557
- api_to_model_map = clearskies.configs.StringDict(default={})
553
+ api_to_model_map = configs.StringDict(default={})
558
554
 
559
555
  """
560
556
  The name of the pagination parameter
561
557
  """
562
- pagination_parameter_name = clearskies.configs.String(default="start")
558
+ pagination_parameter_name = configs.String(default="start")
563
559
 
564
560
  """
565
561
  The expected 'type' of the pagination parameter: must be either 'int' or 'str'
566
562
 
567
563
  Note: this is set as a literal string, not as a type.
568
564
  """
569
- pagination_parameter_type = clearskies.configs.Select(["int", "str"], default="str")
565
+ pagination_parameter_type = configs.Select(["int", "str"], default="str")
570
566
 
571
567
  """
572
568
  The name of the parameter that sets the number of records per page (if empty, setting the page size will not be allowed)
573
569
  """
574
- limit_parameter_name = clearskies.configs.String(default="limit")
570
+ limit_parameter_name = configs.String(default="limit")
575
571
 
576
572
  """
577
573
  The requests instance.
@@ -586,11 +582,11 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
586
582
  _auth_injected = False
587
583
  _response_to_model_map: dict[str, str] = None # type: ignore
588
584
 
589
- @clearskies.decorators.parameters_to_properties
585
+ @decorators.parameters_to_properties
590
586
  def __init__(
591
587
  self,
592
588
  base_url: str,
593
- authentication: clearskies.authentication.Authentication | None = None,
589
+ authentication: Authentication | None = None,
594
590
  model_casing: str = "snake_case",
595
591
  api_casing: str = "snake_case",
596
592
  api_to_model_map: dict[str, str] = {},
@@ -658,7 +654,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
658
654
  """
659
655
  return self.finalize_url(url, data, operation)
660
656
 
661
- def finalize_url_from_query(self, query: clearskies.query.Query, operation: str) -> tuple[str, list[str]]:
657
+ def finalize_url_from_query(self, query: Query, operation: str) -> tuple[str, list[str]]:
662
658
  """
663
659
  Create the URL using a query to fill in any URL parameters.
664
660
 
@@ -671,7 +667,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
671
667
  available_routing_data[condition.column_name] = condition.values[0]
672
668
  return self.finalize_url(query.model_class.destination_name(), available_routing_data, operation)
673
669
 
674
- def create_url(self, data: dict[str, Any], model: clearskies.model.Model) -> tuple[str, list[str]]:
670
+ def create_url(self, data: dict[str, Any], model: Model) -> tuple[str, list[str]]:
675
671
  """
676
672
  Calculate the URL to use for a create requst. Also, return the list of ay data parameters used to construct the URL.
677
673
 
@@ -679,11 +675,11 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
679
675
  """
680
676
  return self.finalize_url_from_data(model.destination_name(), data, "create")
681
677
 
682
- def create_method(self, data: dict[str, Any], model: clearskies.model.Model) -> str:
678
+ def create_method(self, data: dict[str, Any], model: Model) -> str:
683
679
  """Return the request method to use with a create request."""
684
680
  return "POST"
685
681
 
686
- def records_url(self, query: clearskies.query.Query) -> tuple[str, list[str]]:
682
+ def records_url(self, query: Query) -> tuple[str, list[str]]:
687
683
  """
688
684
  Calculate the URL to use for a records request. Also, return the list of any query parameters used to construct the URL.
689
685
 
@@ -691,11 +687,11 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
691
687
  """
692
688
  return self.finalize_url_from_query(query, "records")
693
689
 
694
- def records_method(self, query: clearskies.query.Query) -> str:
690
+ def records_method(self, query: Query) -> str:
695
691
  """Return the request method to use when fetching records from the API."""
696
692
  return "GET"
697
693
 
698
- def count_url(self, query: clearskies.query.Query) -> tuple[str, list[str]]:
694
+ def count_url(self, query: Query) -> tuple[str, list[str]]:
699
695
  """
700
696
  Calculate the URL to use for a request to get a record count.. Also, return the list of any query parameters used to construct the URL.
701
697
 
@@ -703,11 +699,11 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
703
699
  """
704
700
  return self.records_url(query)
705
701
 
706
- def count_method(self, query: clearskies.query.Query) -> str:
702
+ def count_method(self, query: Query) -> str:
707
703
  """Return the request method to use when making a request for a record count."""
708
704
  return self.records_method(query)
709
705
 
710
- def delete_url(self, id: int | str, model: clearskies.model.Model) -> tuple[str, list[str]]:
706
+ def delete_url(self, id: int | str, model: Model) -> tuple[str, list[str]]:
711
707
  """
712
708
  Calculate the URL to use for a delete request. Also, return the list of any query parameters used to construct the URL.
713
709
 
@@ -716,11 +712,11 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
716
712
  model_base_url = model.destination_name().strip("/") + "/" if model.destination_name() else ""
717
713
  return self.finalize_url_from_data(f"{model_base_url}{id}", model.get_raw_data(), "delete")
718
714
 
719
- def delete_method(self, id: int | str, model: clearskies.model.Model) -> str:
715
+ def delete_method(self, id: int | str, model: Model) -> str:
720
716
  """Return the request method to use when deleting records via the API."""
721
717
  return "DELETE"
722
718
 
723
- def update_url(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> tuple[str, list[str]]:
719
+ def update_url(self, id: int | str, data: dict[str, Any], model: Model) -> tuple[str, list[str]]:
724
720
  """
725
721
  Calculate the URL to use for an update request. Also, return the list of any query parameters used to construct the URL.
726
722
 
@@ -729,11 +725,11 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
729
725
  model_base_url = model.destination_name().strip("/") + "/" if model.destination_name() else ""
730
726
  return self.finalize_url_from_data(f"{model_base_url}{id}", {**model.get_raw_data(), **data}, "update")
731
727
 
732
- def update_method(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> str:
728
+ def update_method(self, id: int | str, data: dict[str, Any], model: Model) -> str:
733
729
  """Return the request method to use for an update request."""
734
730
  return "PATCH"
735
731
 
736
- def update(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
732
+ def update(self, id: int | str, data: dict[str, Any], model: Model) -> dict[str, Any]:
737
733
  """Update a record."""
738
734
  data = {**data}
739
735
  (url, used_routing_parameters) = self.update_url(id, data, model)
@@ -748,7 +744,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
748
744
  new_record = {**new_record, **self.map_update_response(response.json(), model)}
749
745
  return new_record
750
746
 
751
- def map_update_response(self, response_data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
747
+ def map_update_response(self, response_data: dict[str, Any], model: Model) -> dict[str, Any]:
752
748
  """
753
749
  Take the response from the API endpoint for an update request and figure out where the data lives/return it to build a new model.
754
750
 
@@ -756,7 +752,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
756
752
  """
757
753
  return self.map_record_response(response_data, model.get_columns(), "update")
758
754
 
759
- def create(self, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
755
+ def create(self, data: dict[str, Any], model: Model) -> dict[str, Any]:
760
756
  """Create a record."""
761
757
  data = {**data}
762
758
  (url, used_routing_parameters) = self.create_url(data, model)
@@ -770,19 +766,17 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
770
766
  return self.map_create_response(response.json(), model)
771
767
  return {}
772
768
 
773
- def map_create_response(self, response_data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
769
+ def map_create_response(self, response_data: dict[str, Any], model: Model) -> dict[str, Any]:
774
770
  return self.map_record_response(response_data, model.get_columns(), "create")
775
771
 
776
- def delete(self, id: int | str, model: clearskies.model.Model) -> bool:
772
+ def delete(self, id: int | str, model: Model) -> bool:
777
773
  (url, used_routing_parameters) = self.delete_url(id, model)
778
774
  request_method = self.delete_method(id, model)
779
775
 
780
776
  response = self.execute_request(url, request_method)
781
777
  return True
782
778
 
783
- def records(
784
- self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
785
- ) -> list[dict[str, Any]]:
779
+ def records(self, query: Query, next_page_data: dict[str, str | int] | None = None) -> list[dict[str, Any]]:
786
780
  self.check_query(query)
787
781
  (url, method, body, headers) = self.build_records_request(query)
788
782
  response = self.execute_request(url, method, json=body, headers=headers)
@@ -791,7 +785,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
791
785
  self.set_next_page_data_from_response(next_page_data, query, response)
792
786
  return records
793
787
 
794
- def build_records_request(self, query: clearskies.query.Query) -> tuple[str, str, dict[str, Any], dict[str, str]]:
788
+ def build_records_request(self, query: Query) -> tuple[str, str, dict[str, Any], dict[str, str]]:
795
789
  (url, used_routing_parameters) = self.records_url(query)
796
790
 
797
791
  (condition_route_id, condition_url_parameters, condition_body_parameters) = (
@@ -825,7 +819,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
825
819
  )
826
820
 
827
821
  def conditions_to_request_parameters(
828
- self, query: clearskies.query.Query, used_routing_parameters: list[str]
822
+ self, query: Query, used_routing_parameters: list[str]
829
823
  ) -> tuple[str, dict[str, str], dict[str, Any]]:
830
824
  route_id = ""
831
825
 
@@ -844,7 +838,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
844
838
 
845
839
  return (route_id, url_parameters, {})
846
840
 
847
- def pagination_to_request_parameters(self, query: clearskies.query.Query) -> tuple[dict[str, str], dict[str, Any]]:
841
+ def pagination_to_request_parameters(self, query: Query) -> tuple[dict[str, str], dict[str, Any]]:
848
842
  url_parameters = {}
849
843
  if query.limit:
850
844
  if not self.limit_parameter_name:
@@ -858,7 +852,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
858
852
 
859
853
  return (url_parameters, {})
860
854
 
861
- def sorts_to_request_parameters(self, query: clearskies.query.Query) -> tuple[dict[str, str], dict[str, Any]]:
855
+ def sorts_to_request_parameters(self, query: Query) -> tuple[dict[str, str], dict[str, Any]]:
862
856
  if not query.sorts:
863
857
  return ({}, {})
864
858
 
@@ -873,7 +867,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
873
867
  )
874
868
 
875
869
  def map_records_response(
876
- self, response_data: Any, query: clearskies.query.Query, query_data: dict[str, Any] | None = None
870
+ self, response_data: Any, query: Query, query_data: dict[str, Any] | None = None
877
871
  ) -> list[dict[str, Any]]:
878
872
  """Take the response from an API endpoint that returns a list of records and find the actual list of records."""
879
873
  columns = query.model_class.get_columns()
@@ -918,7 +912,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
918
912
  )
919
913
 
920
914
  def map_record_response(
921
- self, response_data: dict[str, Any], columns: dict[str, clearskies.column.Column], operation: str
915
+ self, response_data: dict[str, Any], columns: dict[str, Column], operation: str
922
916
  ) -> dict[str, Any]:
923
917
  """
924
918
  Take the response from an API endpoint that returns a single record (typically update and create requests) and return the data for a new model.
@@ -948,7 +942,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
948
942
  def check_dict_and_map_to_model(
949
943
  self,
950
944
  response_data: dict[str, Any],
951
- columns: dict[str, clearskies.column.Column],
945
+ columns: dict[str, Column],
952
946
  query_data: dict[str, Any] = {},
953
947
  ) -> dict[str, Any] | None:
954
948
  """
@@ -987,7 +981,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
987
981
 
988
982
  return {**query_data, **mapped}
989
983
 
990
- def build_response_to_model_map(self, columns: dict[str, clearskies.column.Column]) -> dict[str, str]:
984
+ def build_response_to_model_map(self, columns: dict[str, Column]) -> dict[str, str]:
991
985
  if self._response_to_model_map is not None:
992
986
  return self._response_to_model_map
993
987
 
@@ -1003,7 +997,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
1003
997
  def set_next_page_data_from_response(
1004
998
  self,
1005
999
  next_page_data: dict[str, Any],
1006
- query: clearskies.query.Query,
1000
+ query: Query,
1007
1001
  response: requests.Response, # type: ignore
1008
1002
  ) -> None:
1009
1003
  """
@@ -1042,7 +1036,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
1042
1036
  )
1043
1037
  next_page_data[self.pagination_parameter_name] = query_parameters[self.pagination_parameter_name][0]
1044
1038
 
1045
- def count(self, query: clearskies.query.Query) -> int:
1039
+ def count(self, query: Query) -> int:
1046
1040
  raise NotImplementedError(
1047
1041
  f"The {self.__class__.__name__} backend does not support count operations, so you can't use the `len` or `bool` function for any models using it."
1048
1042
  )
@@ -1104,7 +1098,7 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
1104
1098
 
1105
1099
  return response
1106
1100
 
1107
- def check_query(self, query: clearskies.query.Query) -> None:
1101
+ def check_query(self, query: Query) -> None:
1108
1102
  for key in ["joins", "group_by", "selects"]:
1109
1103
  if getattr(query, key):
1110
1104
  raise ValueError(f"{self.__class__.__name__} does not support queries with {key}")
@@ -1155,20 +1149,20 @@ class ApiBackend(clearskies.configurable.Configurable, Backend, InjectableProper
1155
1149
  )
1156
1150
  ]
1157
1151
 
1158
- def column_from_backend(self, column: clearskies.column.Column, value: Any) -> Any:
1152
+ def column_from_backend(self, column: Column, value: Any) -> Any:
1159
1153
  """We have a couple columns we want to override transformations for."""
1160
1154
  # most importantly, there's no need to transform a JSON column in either direction
1161
- if isinstance(column, clearskies.columns.json.Json):
1155
+ if isinstance(column, columns.json.Json):
1162
1156
  return value
1163
1157
  return super().column_from_backend(column, value)
1164
1158
 
1165
- def column_to_backend(self, column: clearskies.column.Column, backend_data: dict[str, Any]) -> dict[str, Any]:
1159
+ def column_to_backend(self, column: Column, backend_data: dict[str, Any]) -> dict[str, Any]:
1166
1160
  """We have a couple columns we want to override transformations for."""
1167
1161
  # most importantly, there's no need to transform a JSON column in either direction
1168
- if isinstance(column, clearskies.columns.json.Json):
1162
+ if isinstance(column, columns.json.Json):
1169
1163
  return backend_data
1170
1164
  # also, APIs tend to have a different format for dates than SQL
1171
- if isinstance(column, clearskies.columns.datetime.Datetime) and column.name in backend_data:
1165
+ if isinstance(column, columns.datetime.Datetime) and column.name in backend_data:
1172
1166
  as_date = (
1173
1167
  backend_data[column.name].isoformat()
1174
1168
  if type(backend_data[column.name]) != str
@@ -1,12 +1,14 @@
1
- import inspect
1
+ from __future__ import annotations
2
+
2
3
  from abc import ABC, abstractmethod
3
- from typing import Any, Callable, Type
4
+ from typing import TYPE_CHECKING, Any, Callable
4
5
 
5
- import clearskies.column
6
- import clearskies.model
7
- import clearskies.query
8
6
  from clearskies.autodoc.schema import Schema as AutoDocSchema
9
7
 
8
+ if TYPE_CHECKING:
9
+ from clearskies import Column, Model
10
+ from clearskies.query import Query
11
+
10
12
 
11
13
  class Backend(ABC):
12
14
  """
@@ -26,29 +28,27 @@ class Backend(ABC):
26
28
  can_count = True
27
29
 
28
30
  @abstractmethod
29
- def update(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
31
+ def update(self, id: int | str, data: dict[str, Any], model: Model) -> dict[str, Any]:
30
32
  """Update the record with the given id with the information from the data dictionary."""
31
33
  pass
32
34
 
33
35
  @abstractmethod
34
- def create(self, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
36
+ def create(self, data: dict[str, Any], model: Model) -> dict[str, Any]:
35
37
  """Create a record with the information from the data dictionary."""
36
38
  pass
37
39
 
38
40
  @abstractmethod
39
- def delete(self, id: int | str, model: clearskies.model.Model) -> bool:
41
+ def delete(self, id: int | str, model: Model) -> bool:
40
42
  """Delete the record with the given id."""
41
43
  pass
42
44
 
43
45
  @abstractmethod
44
- def count(self, query: clearskies.query.Query) -> int:
46
+ def count(self, query: Query) -> int:
45
47
  """Return the number of records which match the given query configuration."""
46
48
  pass
47
49
 
48
50
  @abstractmethod
49
- def records(
50
- self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
51
- ) -> list[dict[str, Any]]:
51
+ def records(self, query: Query, next_page_data: dict[str, str | int] | None = None) -> list[dict[str, Any]]:
52
52
  """
53
53
  Return a list of records that match the given query configuration.
54
54
 
@@ -107,7 +107,7 @@ class Backend(ABC):
107
107
  """
108
108
  pass
109
109
 
110
- def column_from_backend(self, column: clearskies.column.Column, value: Any) -> Any:
110
+ def column_from_backend(self, column: Column, value: Any) -> Any:
111
111
  """
112
112
  Manage transformations from the backend.
113
113
 
@@ -121,7 +121,7 @@ class Backend(ABC):
121
121
  """
122
122
  return column.from_backend(value)
123
123
 
124
- def column_to_backend(self, column: clearskies.column.Column, backend_data: dict[str, Any]) -> dict[str, Any]:
124
+ def column_to_backend(self, column: Column, backend_data: dict[str, Any]) -> dict[str, Any]:
125
125
  """
126
126
  Manage transformations to the backend.
127
127
 
@@ -1,11 +1,15 @@
1
- from typing import Any, Callable
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Callable
2
4
 
3
- import clearskies.model
4
- import clearskies.query
5
5
  from clearskies.autodoc.schema import Integer as AutoDocInteger
6
6
  from clearskies.autodoc.schema import Schema as AutoDocSchema
7
7
  from clearskies.backends.backend import Backend
8
8
  from clearskies.di import InjectableProperties, inject
9
+ from clearskies.query import Condition, Query
10
+
11
+ if TYPE_CHECKING:
12
+ from clearskies import Model
9
13
 
10
14
 
11
15
  class CursorBackend(Backend, InjectableProperties):
@@ -133,7 +137,7 @@ class CursorBackend(Backend, InjectableProperties):
133
137
  + self.table_escape_character
134
138
  )
135
139
 
136
- def update(self, id: int | str, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
140
+ def update(self, id: int | str, data: dict[str, Any], model: Model) -> dict[str, Any]:
137
141
  query_parts = []
138
142
  parameters = []
139
143
  escape = self.column_escape_character
@@ -149,13 +153,9 @@ class CursorBackend(Backend, InjectableProperties):
149
153
  )
150
154
 
151
155
  # and now query again to fetch the updated record.
152
- return self.records(
153
- clearskies.query.Query(
154
- model.__class__, conditions=[clearskies.query.Condition(f"{model.id_column_name}={id}")]
155
- )
156
- )[0]
156
+ return self.records(Query(model.__class__, conditions=[Condition(f"{model.id_column_name}={id}")]))[0]
157
157
 
158
- def create(self, data: dict[str, Any], model: clearskies.model.Model) -> dict[str, Any]:
158
+ def create(self, data: dict[str, Any], model: Model) -> dict[str, Any]:
159
159
  escape = self.column_escape_character
160
160
  columns = escape + f"{escape}, {escape}".join(data.keys()) + escape
161
161
  placeholders = ", ".join(["%s" for i in range(len(data))])
@@ -168,27 +168,21 @@ class CursorBackend(Backend, InjectableProperties):
168
168
  if not new_id:
169
169
  raise ValueError("I can't figure out what the id is for a newly created record :(")
170
170
 
171
- return self.records(
172
- clearskies.query.Query(
173
- model.__class__, conditions=[clearskies.query.Condition(f"{model.id_column_name}={new_id}")]
174
- )
175
- )[0]
171
+ return self.records(Query(model.__class__, conditions=[Condition(f"{model.id_column_name}={new_id}")]))[0]
176
172
 
177
- def delete(self, id: int | str, model: clearskies.model.Model) -> bool:
173
+ def delete(self, id: int | str, model: Model) -> bool:
178
174
  table_name = self._finalize_table_name(model.destination_name())
179
175
  self.cursor.execute(f"DELETE FROM {table_name} WHERE {model.id_column_name}=%s", (id,))
180
176
  return True
181
177
 
182
- def count(self, query: clearskies.query.Query) -> int:
178
+ def count(self, query: Query) -> int:
183
179
  (sql, parameters) = self.as_count_sql(query)
184
180
  self.cursor.execute(sql, parameters)
185
181
  for row in self.cursor:
186
182
  return row[0] if type(row) == tuple else row["count"]
187
183
  return 0
188
184
 
189
- def records(
190
- self, query: clearskies.query.Query, next_page_data: dict[str, str | int] | None = None
191
- ) -> list[dict[str, Any]]:
185
+ def records(self, query: Query, next_page_data: dict[str, str | int] | None = None) -> list[dict[str, Any]]:
192
186
  # I was going to get fancy and have this return an iterator, but since I'm going to load up
193
187
  # everything into a list anyway, I may as well just return the list, right?
194
188
  (sql, parameters) = self.as_sql(query)
@@ -201,7 +195,7 @@ class CursorBackend(Backend, InjectableProperties):
201
195
  next_page_data["start"] = int(start) + int(limit)
202
196
  return records
203
197
 
204
- def as_sql(self, query: clearskies.query.Query) -> tuple[str, tuple[Any]]:
198
+ def as_sql(self, query: Query) -> tuple[str, tuple[Any]]:
205
199
  escape = self.column_escape_character
206
200
  table_name = query.model_class.destination_name()
207
201
  (wheres, parameters) = self.conditions_as_wheres_and_parameters(
@@ -243,7 +237,7 @@ class CursorBackend(Backend, InjectableProperties):
243
237
  parameters,
244
238
  )
245
239
 
246
- def as_count_sql(self, query: clearskies.query.Query) -> tuple[str, tuple[Any]]:
240
+ def as_count_sql(self, query: Query) -> tuple[str, tuple[Any]]:
247
241
  escape = self.column_escape_character
248
242
  # note that this won't work if we start including a HAVING clause
249
243
  (wheres, parameters) = self.conditions_as_wheres_and_parameters(
@@ -267,7 +261,7 @@ class CursorBackend(Backend, InjectableProperties):
267
261
  return (query_string, parameters)
268
262
 
269
263
  def conditions_as_wheres_and_parameters(
270
- self, conditions: list[clearskies.query.Condition], default_table_name: str
264
+ self, conditions: list[Condition], default_table_name: str
271
265
  ) -> tuple[str, tuple[Any]]:
272
266
  if not conditions:
273
267
  return ("", ()) # type: ignore