ransacklib 1.1.0.dev12__tar.gz → 1.1.0.dev14__tar.gz

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.
Files changed (22) hide show
  1. {ransacklib-1.1.0.dev12/ransacklib.egg-info → ransacklib-1.1.0.dev14}/PKG-INFO +2 -1
  2. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/pyproject.toml +2 -1
  3. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/function.py +4 -1
  4. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/operator.py +23 -10
  5. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/transformer.py +20 -7
  6. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14/ransacklib.egg-info}/PKG-INFO +2 -1
  7. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransacklib.egg-info/SOURCES.txt +1 -0
  8. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransacklib.egg-info/requires.txt +1 -0
  9. ransacklib-1.1.0.dev14/tests/test_postgres.py +1178 -0
  10. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/tests/test_transformer.py +4 -204
  11. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/LICENSE +0 -0
  12. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/README.rst +0 -0
  13. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/__init__.py +0 -0
  14. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/exceptions.py +0 -0
  15. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/parser.py +0 -0
  16. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/py.typed +0 -0
  17. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransack/types.py +0 -0
  18. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransacklib.egg-info/dependency_links.txt +0 -0
  19. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/ransacklib.egg-info/top_level.txt +0 -0
  20. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/setup.cfg +0 -0
  21. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/tests/test_operator.py +0 -0
  22. {ransacklib-1.1.0.dev12 → ransacklib-1.1.0.dev14}/tests/test_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ransacklib
3
- Version: 1.1.0.dev12
3
+ Version: 1.1.0.dev14
4
4
  Summary: A modern, extensible language for manipulation with structured data
5
5
  Author-email: "Rajmund H. Hruška" <rajmund.hruska@cesnet.cz>
6
6
  License-Expression: MIT
@@ -26,6 +26,7 @@ Requires-Dist: mypy<2.0,>=1.18; extra == "dev"
26
26
  Requires-Dist: ruff<0.15,>=0.14; extra == "dev"
27
27
  Requires-Dist: build<1.4,>=1.3; extra == "dev"
28
28
  Requires-Dist: twine<7.0,>=6.2; extra == "dev"
29
+ Requires-Dist: psycopg[binary]; extra == "dev"
29
30
  Dynamic: license-file
30
31
 
31
32
  Welcome to ransack
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ransacklib"
7
- version = "1.1.0.dev12"
7
+ version = "1.1.0.dev14"
8
8
  description = "A modern, extensible language for manipulation with structured data"
9
9
  license = "MIT"
10
10
  readme = "README.rst"
@@ -33,6 +33,7 @@ dev = [
33
33
  "ruff>=0.14,<0.15",
34
34
  "build>=1.3,<1.4",
35
35
  "twine>=6.2,<7.0",
36
+ "psycopg[binary]",
36
37
  ]
37
38
 
38
39
  [project.urls]
@@ -17,12 +17,15 @@ Example usage in query:
17
17
  """
18
18
 
19
19
  import datetime
20
+ from collections.abc import MutableSequence
20
21
 
21
22
  from ipranges import IP4, IP6, IP4Net, IP4Range, IP6Net, IP6Range
22
23
 
23
24
 
24
25
  def length(x):
25
- if isinstance(x, (list, str, IP4, IP4Net, IP4Range, IP6, IP6Net, IP6Range)):
26
+ if isinstance(
27
+ x, (MutableSequence, str, IP4, IP4Net, IP4Range, IP6, IP6Net, IP6Range)
28
+ ):
26
29
  return len(x)
27
30
  raise TypeError(f"Function length is not defined for value {x}")
28
31
 
@@ -607,20 +607,16 @@ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, typing.Any, bool]:
607
607
  (Number, Number): (f"{l_sql} {op} {r_sql}", False),
608
608
  (Number, List(Number)): (f"{l_sql} {op} ANY({r_sql})", False),
609
609
  (Number, Range(Number)): (f"{l_sql} {op} {tuple_repr_f}({r_sql})", False),
610
- (List(Number), Number): (f"ANY({l_sql}) {op} {r_sql}", False),
611
610
  (Datetime, Datetime): (f"{l_sql} {op} {r_sql}", False),
612
611
  (Datetime, List(Datetime)): (f"{l_sql} {op} ANY({r_sql})", False),
613
612
  (Datetime, Range(Datetime)): (
614
613
  f"{r_sql} {op} {tuple_repr_f}({l_sql})",
615
614
  True,
616
615
  ),
617
- (List(Datetime), Datetime): (f"ANY({l_sql}) {op} {r_sql}", False),
618
616
  (Timedelta, Timedelta): (f"{l_sql} {op} {r_sql}", False),
619
617
  (Timedelta, List(Timedelta)): (f"{l_sql} {op} ANY({r_sql})", False),
620
- (List(Timedelta), Timedelta): (f"ANY({l_sql}) {op} {r_sql}", False),
621
618
  (IP, IP): (f"{l_sql} {op} {r_sql}", False),
622
619
  (IP, List(IP)): (f"{l_sql} {op} ANY({r_sql})", False),
623
- (List(IP), IP): (f"ANY({l_sql}) {op} {r_sql}", False),
624
620
  }
625
621
 
626
622
  # Comparison and logical operators (result is always boolean).
@@ -639,6 +635,7 @@ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, typing.Any, bool]:
639
635
  (String, List(String)): (f"{l_sql} = ANY({r_sql})", False),
640
636
  (IP, IP): (f"{l_sql} && {r_sql}", False),
641
637
  (IP, List(IP)): (f"{l_sql} && ANY({r_sql})", False),
638
+ (List(IP), IP): (f"{r_sql} && ANY({l_sql})", True),
642
639
  (List(Any), List(Any)): (f"{l_sql} && {r_sql}", False),
643
640
  (Range(Any), Range(Any)): (f"{l_sql} && {r_sql}", False),
644
641
  },
@@ -647,24 +644,40 @@ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, typing.Any, bool]:
647
644
  (Range(Any), Range(Any)): (f"upper({l_sql}) > lower({r_sql})", False),
648
645
  (Range(Number), Number): (f"upper({l_sql}) > {r_sql}", False),
649
646
  (Range(Datetime), Datetime): (f"upper({l_sql}) > {r_sql}", False),
647
+ (List(Number), Number): (f"{r_sql} < ANY({l_sql})", True),
648
+ (List(Datetime), Datetime): (f"{r_sql} < ANY({l_sql})", True),
649
+ (List(Timedelta), Timedelta): (f"{r_sql} < ANY({l_sql})", True),
650
+ (List(IP), IP): (f"{r_sql} < ANY({l_sql})", True),
650
651
  },
651
652
  ">=": _get_comp_dict_sql(">=", "lower")
652
653
  | {
653
654
  (Range(Any), Range(Any)): (f"upper({l_sql}) >= lower({r_sql})", False),
654
655
  (Range(Number), Number): (f"upper({l_sql}) >= {r_sql}", False),
655
656
  (Range(Datetime), Datetime): (f"upper({l_sql}) >= {r_sql}", False),
657
+ (List(Number), Number): (f"{r_sql} <= ANY({l_sql})", True),
658
+ (List(Datetime), Datetime): (f"{r_sql} <= ANY({l_sql})", True),
659
+ (List(Timedelta), Timedelta): (f"{r_sql} <= ANY({l_sql})", True),
660
+ (List(IP), IP): (f"{r_sql} <= ANY({l_sql})", True),
656
661
  },
657
662
  "<": _get_comp_dict_sql("<", "upper")
658
663
  | {
659
664
  (Range(Any), Range(Any)): (f"lower({l_sql}) < upper({r_sql})", False),
660
665
  (Range(Number), Number): (f"lower({l_sql}) < {r_sql}", False),
661
666
  (Range(Datetime), Datetime): (f"lower({l_sql}) < {r_sql}", False),
667
+ (List(Number), Number): (f"{r_sql} > ANY({l_sql})", True),
668
+ (List(Datetime), Datetime): (f"{r_sql} > ANY({l_sql})", True),
669
+ (List(Timedelta), Timedelta): (f"{r_sql} > ANY({l_sql})", True),
670
+ (List(IP), IP): (f"{r_sql} > ANY({l_sql})", True),
662
671
  },
663
672
  "<=": _get_comp_dict_sql("<=", "upper")
664
673
  | {
665
674
  (Range(Any), Range(Any)): (f"lower({l_sql}) <= upper({r_sql})", False),
666
675
  (Range(Number), Number): (f"lower({l_sql}) <= {r_sql}", False),
667
676
  (Range(Datetime), Datetime): (f"lower({l_sql}) <= {r_sql}", False),
677
+ (List(Number), Number): (f"{r_sql} >= ANY({l_sql})", True),
678
+ (List(Datetime), Datetime): (f"{r_sql} >= ANY({l_sql})", True),
679
+ (List(Timedelta), Timedelta): (f"{r_sql} >= ANY({l_sql})", True),
680
+ (List(IP), IP): (f"{r_sql} >= ANY({l_sql})", True),
668
681
  },
669
682
  "and": {(Bool, Bool): (f"{l_sql} AND {r_sql}", False)},
670
683
  "or": {(Bool, Bool): (f"({l_sql} OR {r_sql})", False)},
@@ -743,28 +756,28 @@ def _operator_map_sql(op, l_sql, r_sql, t1, t2) -> tuple[str, typing.Any, bool]:
743
756
  ),
744
757
  },
745
758
  "*": {
746
- (Timedelta, Number): (f"{l_sql} * {r_sql}", Timedelta),
759
+ (Timedelta, Number): (f"({l_sql} * {r_sql})", Timedelta),
747
760
  (Timedelta, List(Number)): (
748
761
  f"array(select {l_sql} * unnest({r_sql}))",
749
762
  List(Timedelta),
750
763
  ),
751
- (Number, Number): (f"{l_sql} * {r_sql}", Number),
764
+ (Number, Number): (f"({l_sql} * {r_sql})", Number),
752
765
  (Number, List(Number)): (
753
766
  f"array(select {l_sql} * unnest({r_sql}))",
754
767
  List(Number),
755
768
  ),
756
769
  },
757
770
  "/": {
758
- (Timedelta, Timedelta): (f"{l_sql} / {r_sql}", Timedelta),
759
- (Timedelta, List(Timedelta)): (
771
+ (Timedelta, Number): (f"({l_sql} / {r_sql})", Timedelta),
772
+ (Timedelta, List(Number)): (
760
773
  f"array(select {l_sql} / unnest({r_sql}))",
761
774
  List(Timedelta),
762
775
  ),
763
- (List(Timedelta), Timedelta): (
776
+ (List(Timedelta), Number): (
764
777
  f"array(select unnest({l_sql}) / {r_sql})",
765
778
  List(Timedelta),
766
779
  ),
767
- (Number, Number): (f"{l_sql} / {r_sql}", Number),
780
+ (Number, Number): (f"({l_sql} / {r_sql})", Number),
768
781
  (Number, List(Number)): (
769
782
  f"array(select {l_sql} / unnest({r_sql}))",
770
783
  List(Number),
@@ -853,8 +853,9 @@ class SQLInterpreter(Interpreter):
853
853
  return result, params, type_
854
854
 
855
855
  def number(self, token: TokenWrapper) -> tuple[str, list, types.RansackType]:
856
- type_ = types.Integer if isinstance(token.real_value, int) else types.Float
857
- return ("%s", [token.real_value], type_)
856
+ if isinstance(token.real_value, int):
857
+ return ("%s::int", [token.real_value], types.Integer)
858
+ return ("%s", [token.real_value], types.Float)
858
859
 
859
860
  def datetime(self, token: TokenWrapper) -> tuple[str, list, types.RansackType]:
860
861
  if token.real_value.tzinfo is not None:
@@ -893,6 +894,8 @@ class SQLInterpreter(Interpreter):
893
894
  elif l_type is types.Float and r_type is types.Float:
894
895
  range_type = "numrange"
895
896
  l_type = types.Number
897
+ l_sql = l_sql + "::numeric"
898
+ r_sql = r_sql + "::numeric"
896
899
  elif l_type is types.Datetime and r_type is types.Datetime:
897
900
  if "WITH TIME ZONE" in l_sql and "WITH TIME ZONE" in r_sql:
898
901
  range_type = "tstzrange"
@@ -950,20 +953,30 @@ class SQLInterpreter(Interpreter):
950
953
 
951
954
  return sql, params, type_
952
955
 
956
+ def _arithmetic_operation(self, operator, l_tree, r_tree) -> tuple[str, list, Any]:
957
+ l_sql, l_params, l_type = self.visit(l_tree)
958
+ r_sql, r_params, r_type = self.visit(r_tree)
959
+
960
+ if l_type == types.List(types.Empty):
961
+ return l_sql, l_params, l_type
962
+ if r_type == types.List(types.Empty):
963
+ return r_sql, r_params, r_type
964
+ return self._binary_operation(operator, l_tree, r_tree)
965
+
953
966
  def add(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
954
- return self._binary_operation("+", l_tree, r_tree)
967
+ return self._arithmetic_operation("+", l_tree, r_tree)
955
968
 
956
969
  def sub(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
957
- return self._binary_operation("-", l_tree, r_tree)
970
+ return self._arithmetic_operation("-", l_tree, r_tree)
958
971
 
959
972
  def mul(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
960
- return self._binary_operation("*", l_tree, r_tree)
973
+ return self._arithmetic_operation("*", l_tree, r_tree)
961
974
 
962
975
  def div(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
963
- return self._binary_operation("/", l_tree, r_tree)
976
+ return self._arithmetic_operation("/", l_tree, r_tree)
964
977
 
965
978
  def mod(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, Any]:
966
- return self._binary_operation("%", l_tree, r_tree)
979
+ return self._arithmetic_operation("%", l_tree, r_tree)
967
980
 
968
981
  def eq(self, l_tree: Tree, r_tree: Tree) -> tuple[str, list, type[bool]]:
969
982
  return self._binary_operation("==", l_tree, r_tree)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ransacklib
3
- Version: 1.1.0.dev12
3
+ Version: 1.1.0.dev14
4
4
  Summary: A modern, extensible language for manipulation with structured data
5
5
  Author-email: "Rajmund H. Hruška" <rajmund.hruska@cesnet.cz>
6
6
  License-Expression: MIT
@@ -26,6 +26,7 @@ Requires-Dist: mypy<2.0,>=1.18; extra == "dev"
26
26
  Requires-Dist: ruff<0.15,>=0.14; extra == "dev"
27
27
  Requires-Dist: build<1.4,>=1.3; extra == "dev"
28
28
  Requires-Dist: twine<7.0,>=6.2; extra == "dev"
29
+ Requires-Dist: psycopg[binary]; extra == "dev"
29
30
  Dynamic: license-file
30
31
 
31
32
  Welcome to ransack
@@ -16,4 +16,5 @@ ransacklib.egg-info/requires.txt
16
16
  ransacklib.egg-info/top_level.txt
17
17
  tests/test_operator.py
18
18
  tests/test_parser.py
19
+ tests/test_postgres.py
19
20
  tests/test_transformer.py
@@ -10,3 +10,4 @@ mypy<2.0,>=1.18
10
10
  ruff<0.15,>=0.14
11
11
  build<1.4,>=1.3
12
12
  twine<7.0,>=6.2
13
+ psycopg[binary]