@@ -652,98 +652,136 @@ def x(self):
652652 self .call_counter ["x" ] += 1
653653 return 1
654654
655- @field
655+ @field ( disabled = False )
656656 def y (self ):
657657 self .call_counter ["y" ] += 1
658658 return 2
659659
660- @field
660+ @field ( disabled = True )
661661 def z (self ):
662662 self .call_counter ["z" ] += 1
663663 return 3
664664
665665
666666@pytest .mark .asyncio
667- async def test_select_fields_include () -> None :
668- # Basic case
669- page = BigPage (SelectFields (include = ["x" , "y" ]))
667+ async def test_select_fields () -> None :
668+ # Required fields from the item cls which are not included raise an TypeError
669+ expected_type_error_msg = (
670+ r"__init__\(\) missing 1 required positional argument: 'x'"
671+ )
672+
673+ # When SelectFields isn't set, it should simply extract the non-disabled
674+ # fields.
675+ page = BigPage ()
676+ item = await page .to_item ()
677+ assert item == BigItem (x = 1 , y = 2 , z = None )
678+ assert page .fields_to_extract == ["x" , "y" ]
679+ assert page .call_counter == {"x" : 1 , "y" : 1 }
680+
681+ # If no field selection directive is given but SelectFields is set, it would
682+ # use the default fields that are not disabled.
683+ page = BigPage (SelectFields (fields = None ))
684+ item = await page .to_item ()
685+ assert item == BigItem (x = 1 , y = 2 , z = None )
686+ assert page .fields_to_extract == ["x" , "y" ]
687+ assert page .call_counter == {"x" : 1 , "y" : 1 }
688+
689+ # Same case as above but given an empty dict.
690+ page = BigPage (SelectFields (fields = {}))
670691 item = await page .to_item ()
671692 assert item == BigItem (x = 1 , y = 2 , z = None )
672- assert page .fields_to_extract == { "x" , "y" }
693+ assert page .fields_to_extract == [ "x" , "y" ]
673694 assert page .call_counter == {"x" : 1 , "y" : 1 }
674695
675- # Repeated fields are ignored
676- page = BigPage (SelectFields (include = ["x" , "x" ]))
696+ # Select all fields
697+ page = BigPage (SelectFields (fields = {"*" : True }))
698+ item = await page .to_item ()
699+ assert item == BigItem (x = 1 , y = 2 , z = 3 )
700+ assert page .fields_to_extract == ["x" , "y" , "z" ]
701+ assert page .call_counter == {"x" : 1 , "y" : 1 , "z" : 1 }
702+
703+ # Don't select all fields
704+ page = BigPage (SelectFields (fields = {"*" : False }))
705+ with pytest .raises (TypeError , match = expected_type_error_msg ):
706+ await page .to_item ()
707+ assert page .fields_to_extract == []
708+ assert page .call_counter == {}
709+
710+ # Exclude all but one
711+ page = BigPage (SelectFields (fields = {"*" : False , "x" : True }))
677712 item = await page .to_item ()
678713 assert item == BigItem (x = 1 , y = None , z = None )
679- assert page .fields_to_extract == { "x" }
714+ assert page .fields_to_extract == [ "x" ]
680715 assert page .call_counter == {"x" : 1 }
681716
682- # Passing None value results in all fields to be extracted.
683- # Note that this is different when [] is passed. See test below.
684- page = BigPage (SelectFields (include = None ))
717+ # Include all fields but one
718+ page = BigPage (SelectFields (fields = {"*" : True , "z" : False }))
719+ item = await page .to_item ()
720+ assert item == BigItem (x = 1 , y = 2 , z = None )
721+ assert page .fields_to_extract == ["x" , "y" ]
722+ assert page .call_counter == {"x" : 1 , "y" : 1 }
723+
724+ # overlapping directives on the same field should be okay
725+ page = BigPage (SelectFields (fields = {"*" : True , "x" : True , "y" : True , "z" : True }))
685726 item = await page .to_item ()
686727 assert item == BigItem (x = 1 , y = 2 , z = 3 )
687- assert page .fields_to_extract == { "x" , "y" , "z" }
728+ assert page .fields_to_extract == [ "x" , "y" , "z" ]
688729 assert page .call_counter == {"x" : 1 , "y" : 1 , "z" : 1 }
689730
690- # Required fields from the item cls which are not included raise an TypeError
691- expected_type_error_msg = (
692- r"__init__\(\) missing 1 required positional argument: 'x'"
693- )
694- page = BigPage (SelectFields (include = []))
731+ # Exluding a required field throws an error
732+ page = BigPage (SelectFields (fields = {"x" : False }))
695733 with pytest .raises (TypeError , match = expected_type_error_msg ):
696734 item = await page .to_item ()
697- assert page .fields_to_extract == set ()
698- assert page .call_counter == {}
735+ assert page .fields_to_extract == [ "y" ]
736+ assert page .call_counter == {"y" : 1 }
699737
700- page = BigPage (SelectFields (include = ["y" , "z" ]))
701- with pytest .raises (TypeError , match = expected_type_error_msg ):
702- await page .to_item ()
703- assert page .fields_to_extract == {"y" , "z" }
704- assert page .call_counter == {"y" : 1 , "z" : 1 }
738+ # boolean-like values are not supported. They are simply ignored and the
739+ # page would revert back to the default field directives.
740+ page = BigPage (SelectFields (fields = {"x" : 0 , "y" : 0 , "z" : 1 })) # type: ignore[dict-item]
741+ item = await page .to_item ()
742+ assert item == BigItem (x = 1 , y = 2 , z = None )
743+ assert page .fields_to_extract == ["x" , "y" ]
744+ assert page .call_counter == {"x" : 1 , "y" : 1 }
705745
706746 # The remaining tests below checks the different behaviors when encountering a
707747 # field which doesn't existing in the PO
708- fields = [ "x" , "not_existing" ]
748+ fields = { "x" : True , "not_existing" : True }
709749 expected_attribute_error_msg = (
710- "Field 'not_existing' isn't available in tests.test_fields.BigPage"
750+ "The { 'not_existing'} fields isn't available in tests.test_fields.BigPage"
711751 )
712752
713753 # Unknown field raises an AttributeError by default
714- page = BigPage (SelectFields (include = fields ))
754+ page = BigPage (SelectFields (fields = fields ))
715755 with pytest .raises (AttributeError , match = expected_attribute_error_msg ):
716756 await page .to_item ()
717- assert page .fields_to_extract == set (fields )
757+ with pytest .raises (AttributeError , match = expected_attribute_error_msg ):
758+ assert page .fields_to_extract
718759
719- page = BigPage (SelectFields (include = fields , on_unknown_field = "raise" ))
760+ page = BigPage (SelectFields (fields = fields , on_unknown_field = "raise" ))
720761 with pytest .raises (AttributeError , match = expected_attribute_error_msg ):
721762 await page .to_item ()
722- assert page .fields_to_extract == set (fields )
763+ with pytest .raises (AttributeError , match = expected_attribute_error_msg ):
764+ assert page .fields_to_extract
723765
724- # It should safely ignore it if page object has set skip_nonitem_fields
725- page = BigPage (SelectFields (include = fields , on_unknown_field = "ignore" ))
766+ # It should safely ignore it as well.
767+ page = BigPage (SelectFields (fields = fields , on_unknown_field = "ignore" ))
726768 with warnings .catch_warnings (record = True ) as caught_warnings :
727769 item = await page .to_item ()
728- assert item == BigItem (x = 1 , y = None , z = None )
729770 assert not caught_warnings
730- assert page .fields_to_extract == set (fields )
731- assert page .call_counter == {"x" : 1 }
771+ assert item == BigItem (x = 1 , y = 2 , z = None )
772+ with warnings .catch_warnings (record = True ) as caught_warnings :
773+ assert page .fields_to_extract == ["x" , "y" ]
774+ assert not caught_warnings
775+ assert page .call_counter == {"x" : 1 , "y" : 1 }
732776
733777 # When 'warn' is used, the same msg when 'raise' is used.
734- page = BigPage (SelectFields (include = fields , on_unknown_field = "warn" ))
735- with warnings . catch_warnings ( record = True ) as caught_warnings :
778+ page = BigPage (SelectFields (fields = fields , on_unknown_field = "warn" ))
779+ with pytest . warns ( UserWarning , match = expected_attribute_error_msg ) :
736780 item = await page .to_item ()
737- assert item == BigItem (x = 1 , y = None , z = None )
738- assert any (
739- [
740- True
741- for w in caught_warnings
742- if expected_attribute_error_msg in str (w .message )
743- ]
744- )
745- assert page .fields_to_extract == set (fields )
746- assert page .call_counter == {"x" : 1 }
781+ assert item == BigItem (x = 1 , y = 2 , z = None )
782+ with pytest .warns (UserWarning , match = expected_attribute_error_msg ):
783+ assert page .fields_to_extract == ["x" , "y" ]
784+ assert page .call_counter == {"x" : 1 , "y" : 1 }
747785
748786
749787@pytest .mark .asyncio
@@ -759,147 +797,5 @@ async def test_select_fields_on_unknown_field_bad_value() -> None:
759797 with pytest .raises (ValueError , match = expected_value_error_msg ):
760798 await BigPage (
761799 # ignore mypy error since it's expecting a valid value inside the Literal.
762- SelectFields (include = ["y" , "not_existing" ], on_unknown_field = invalid_val ) # type: ignore[arg-type]
800+ SelectFields (fields = ["y" , "not_existing" ], on_unknown_field = invalid_val ) # type: ignore[arg-type]
763801 ).to_item ()
764-
765-
766- @pytest .mark .asyncio
767- async def test_select_fields_exclude () -> None :
768- # Basic case
769- page = BigPage (SelectFields (exclude = ["y" , "z" ]))
770- item = await page .to_item ()
771- assert item == BigItem (x = 1 , y = None , z = None )
772- assert page .fields_to_extract == {"x" }
773- assert page .call_counter == {"x" : 1 }
774-
775- # Repeated fields are ignored
776- page = BigPage (SelectFields (exclude = ["y" , "y" ]))
777- item = await page .to_item ()
778- assert item == BigItem (x = 1 , y = None , z = 3 )
779- assert page .fields_to_extract == {"x" , "z" }
780- assert page .call_counter == {"x" : 1 , "z" : 1 }
781-
782- # A value of None would return all fields
783- page = BigPage (SelectFields (exclude = None ))
784- item = await page .to_item ()
785- assert item == BigItem (x = 1 , y = 2 , z = 3 )
786- assert page .fields_to_extract == {"x" , "y" , "z" }
787- assert page .call_counter == {"x" : 1 , "y" : 1 , "z" : 1 }
788-
789- # Using an empty list returns all fields
790- page = BigPage (SelectFields (exclude = []))
791- item = await page .to_item ()
792- assert item == BigItem (x = 1 , y = 2 , z = 3 )
793- assert page .fields_to_extract == {"x" , "y" , "z" }
794- assert page .call_counter == {"x" : 1 , "y" : 1 , "z" : 1 }
795-
796- # Required fields from the item cls which are not included raise an TypeError
797- expected_type_error_msg = (
798- r"__init__\(\) missing 1 required positional argument: 'x'"
799- )
800- with pytest .raises (TypeError , match = expected_type_error_msg ):
801- page = BigPage (SelectFields (exclude = ["x" ]))
802- await page .to_item ()
803- assert page .fields_to_extract == {"y" , "z" }
804- assert page .call_counter == {"y" : 1 , "z" : 1 }
805-
806- # Unlike the test setup in ``test_select_fields_include()``, we don't
807- # expect any errors here since 'exclude' actually removes them. However, if
808- # include and exclude were used together, and include introduced an unknown
809- # field which exclude hasn't removed, it should err out.
810- # See ``test_select_fields_include_exclude()``.
811- fields = ["y" , "not_existing" ]
812-
813- page = BigPage (SelectFields (exclude = fields ))
814- item = await page .to_item ()
815- assert item == BigItem (x = 1 , y = None , z = 3 )
816- assert page .fields_to_extract == {"x" , "z" }
817- assert page .call_counter == {"x" : 1 , "z" : 1 }
818-
819- page = BigPage (SelectFields (exclude = fields , on_unknown_field = "raise" ))
820- item = await page .to_item ()
821- assert item == BigItem (x = 1 , y = None , z = 3 )
822- assert page .fields_to_extract == {"x" , "z" }
823- assert page .call_counter == {"x" : 1 , "z" : 1 }
824-
825- page = BigPage (SelectFields (exclude = fields , on_unknown_field = "ignore" ))
826- item = await page .to_item ()
827- assert item == BigItem (x = 1 , y = None , z = 3 )
828- assert page .fields_to_extract == {"x" , "z" }
829- assert page .call_counter == {"x" : 1 , "z" : 1 }
830-
831- with warnings .catch_warnings (record = True ) as caught_warnings :
832- page = BigPage (SelectFields (exclude = fields , on_unknown_field = "warn" ))
833- item = await page .to_item ()
834- assert item == BigItem (x = 1 , y = None , z = 3 )
835- assert not caught_warnings
836- assert page .fields_to_extract == {"x" , "z" }
837- assert page .call_counter == {"x" : 1 , "z" : 1 }
838-
839-
840- @pytest .mark .asyncio
841- async def test_select_fields_include_exclude () -> None :
842- page = BigPage (SelectFields (include = ["x" , "y" ], exclude = ["y" ]))
843- item = await page .to_item ()
844- assert item == BigItem (x = 1 , y = None , z = None )
845- assert page .fields_to_extract == {"x" }
846- assert page .call_counter == {"x" : 1 }
847-
848- # If the fields cancel out, then any required field should error out.
849- expected_type_error_msg = (
850- r"__init__\(\) missing 1 required positional argument: 'x'"
851- )
852- page = BigPage (SelectFields (include = ["x" , "y" ], exclude = ["x" , "y" ]))
853- with pytest .raises (TypeError , match = expected_type_error_msg ):
854- item = await page .to_item ()
855- assert page .fields_to_extract == set ()
856- assert page .call_counter == {}
857-
858- page = BigPage (
859- SelectFields (include = ["x" , "not_existing" ], exclude = ["not_existing" ])
860- )
861- item = await page .to_item ()
862- assert item == BigItem (x = 1 , y = None , z = None )
863- assert page .fields_to_extract == {"x" }
864- assert page .call_counter == {"x" : 1 }
865-
866- page = BigPage (SelectFields (include = ["x" , "y" , "not_existing" ], exclude = ["y" ]))
867- expected_attribute_error_msg = (
868- "Field 'not_existing' isn't available in tests.test_fields.BigPage"
869- )
870- with pytest .raises (AttributeError , match = expected_attribute_error_msg ):
871- await page .to_item ()
872- assert page .fields_to_extract == {"x" , "not_existing" }
873-
874- page = BigPage (SelectFields (include = None , exclude = ["y" ]))
875- item = await page .to_item ()
876- assert item == BigItem (x = 1 , y = None , z = 3 )
877- assert page .fields_to_extract == {"x" , "z" }
878- assert page .call_counter == {"x" : 1 , "z" : 1 }
879-
880- page = BigPage (SelectFields (include = None , exclude = None ))
881- item = await page .to_item ()
882- assert item == BigItem (x = 1 , y = 2 , z = 3 )
883- assert page .fields_to_extract == {"x" , "y" , "z" }
884- assert page .call_counter == {"x" : 1 , "y" : 1 , "z" : 1 }
885-
886- page = BigPage (SelectFields (include = None , exclude = []))
887- item = await page .to_item ()
888- assert item == BigItem (x = 1 , y = 2 , z = 3 )
889- assert page .fields_to_extract == {"x" , "y" , "z" }
890- assert page .call_counter == {"x" : 1 , "y" : 1 , "z" : 1 }
891-
892- for exclude in (["y" ], [], None ):
893- # Ignore some of the types below since mypy is not expecting an empty list
894-
895- page = BigPage (SelectFields (include = [], exclude = exclude )) # type: ignore[arg-type]
896- with pytest .raises (TypeError , match = expected_type_error_msg ):
897- item = await page .to_item ()
898- assert page .fields_to_extract == set ()
899- assert page .call_counter == {}
900-
901- page = BigPage (SelectFields (include = ["x" , "z" ], exclude = exclude )) # type: ignore[arg-type]
902- item = await page .to_item ()
903- assert item == BigItem (x = 1 , y = None , z = 3 )
904- assert page .fields_to_extract == {"x" , "z" }
905- assert page .call_counter == {"x" : 1 , "z" : 1 }
0 commit comments