1717
1818import  unittest 
1919import  requests_cache 
20- from  typing  import  Union , Any 
20+ from  typing  import  Union , Any ,  get_args ,  _GenericAlias 
2121from  urllib .parse  import  urlparse , parse_qs , urlencode , urlunparse 
2222
2323ticker_attributes  =  (
3131    ("actions" , pd .DataFrame ),
3232    ("shares" , pd .DataFrame ),
3333    ("info" , dict ),
34-     ("calendar" , pd . DataFrame ),
34+     ("calendar" , dict ),
3535    ("recommendations" , Union [pd .DataFrame , dict ]),
3636    ("recommendations_summary" , Union [pd .DataFrame , dict ]),
3737    ("upgrades_downgrades" , Union [pd .DataFrame , dict ]),
38-     ("recommendations_history" , Union [pd .DataFrame , dict ]),
3938    ("earnings" , pd .DataFrame ),
4039    ("quarterly_earnings" , pd .DataFrame ),
4140    ("quarterly_cashflow" , pd .DataFrame ),
@@ -58,7 +57,12 @@ def assert_attribute_type(testClass: unittest.TestCase, instance, attribute_name
5857    try :
5958        attribute  =  getattr (instance , attribute_name )
6059        if  attribute  is  not   None  and  expected_type  is  not   Any :
61-             testClass .assertEqual (type (attribute ), expected_type )
60+             err_msg  =  f'{ attribute_name }   type is { type (attribute )}   not { expected_type }  ' 
61+             if  isinstance (expected_type , _GenericAlias ) and  expected_type .__origin__  is  Union :
62+                 allowed_types  =  get_args (expected_type )
63+                 testClass .assertTrue (isinstance (attribute , allowed_types ), err_msg )
64+             else :
65+                 testClass .assertEqual (type (attribute ), expected_type , err_msg )
6266    except  Exception :
6367        testClass .assertRaises (
6468            YFNotImplementedError , lambda : getattr (instance , attribute_name )
@@ -136,8 +140,8 @@ def test_goodTicker_withProxy(self):
136140        tkr  =  "IBM" 
137141        dat  =  yf .Ticker (tkr , session = self .session , proxy = self .proxy )
138142
139-         dat ._fetch_ticker_tz (timeout = 5 )
140-         dat ._get_ticker_tz (timeout = 5 )
143+         dat ._fetch_ticker_tz (proxy = None ,  timeout = 5 )
144+         dat ._get_ticker_tz (proxy = None ,  timeout = 5 )
141145        dat .history (period = "1wk" )
142146
143147        for  attribute_name , attribute_type  in  ticker_attributes :
@@ -654,6 +658,24 @@ def test_cash_flow_alt_names(self):
654658    def  test_bad_freq_value_raises_exception (self ):
655659        self .assertRaises (ValueError , lambda : self .ticker .get_cashflow (freq = "badarg" ))
656660
661+     def  test_calendar (self ):
662+         data  =  self .ticker .calendar 
663+         self .assertIsInstance (data , dict , "data has wrong type" )
664+         self .assertTrue (len (data ) >  0 , "data is empty" )
665+         self .assertIn ("Earnings Date" , data .keys (), "data missing expected key" )
666+         self .assertIn ("Earnings Average" , data .keys (), "data missing expected key" )
667+         self .assertIn ("Earnings Low" , data .keys (), "data missing expected key" )
668+         self .assertIn ("Earnings High" , data .keys (), "data missing expected key" )
669+         self .assertIn ("Revenue Average" , data .keys (), "data missing expected key" )
670+         self .assertIn ("Revenue Low" , data .keys (), "data missing expected key" )
671+         self .assertIn ("Revenue High" , data .keys (), "data missing expected key" )
672+         # dividend date is not available for tested ticker GOOGL 
673+         if  self .ticker .ticker  !=  "GOOGL" :
674+             self .assertIn ("Dividend Date" , data .keys (), "data missing expected key" )
675+         # ex-dividend date is not always available 
676+         data_cached  =  self .ticker .calendar 
677+         self .assertIs (data , data_cached , "data not cached" )
678+ 
657679    # Below will fail because not ported to Yahoo API 
658680
659681    # def test_sustainability(self): 
@@ -664,6 +686,30 @@ def test_bad_freq_value_raises_exception(self):
664686    #     data_cached = self.ticker.sustainability 
665687    #     self.assertIs(data, data_cached, "data not cached") 
666688
689+     # def test_shares(self): 
690+     #     data = self.ticker.shares 
691+     #     self.assertIsInstance(data, pd.DataFrame, "data has wrong type") 
692+     #     self.assertFalse(data.empty, "data is empty") 
693+ 
694+ 
695+ class  TestTickerAnalysts (unittest .TestCase ):
696+     session  =  None 
697+ 
698+     @classmethod  
699+     def  setUpClass (cls ):
700+         cls .session  =  session_gbl 
701+ 
702+     @classmethod  
703+     def  tearDownClass (cls ):
704+         if  cls .session  is  not   None :
705+             cls .session .close ()
706+ 
707+     def  setUp (self ):
708+         self .ticker  =  yf .Ticker ("GOOGL" , session = self .session )
709+         
710+     def  tearDown (self ):
711+         self .ticker  =  None 
712+ 
667713    def  test_recommendations (self ):
668714        data  =  self .ticker .recommendations 
669715        data_summary  =  self .ticker .recommendations_summary 
@@ -674,18 +720,16 @@ def test_recommendations(self):
674720        data_cached  =  self .ticker .recommendations 
675721        self .assertIs (data , data_cached , "data not cached" )
676722
677-     #  def test_recommendations_summary(self):  # currently alias for recommendations
678-     #      data = self.ticker.recommendations_summary
679-     #      self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
680-     #      self.assertFalse(data.empty, "data is empty")
723+     def  test_recommendations_summary (self ):  # currently alias for recommendations 
724+         data  =  self .ticker .recommendations_summary 
725+         self .assertIsInstance (data , pd .DataFrame , "data has wrong type" )
726+         self .assertFalse (data .empty , "data is empty" )
681727
682-     #      data_cached = self.ticker.recommendations_summary
683-     #      self.assertIs(data, data_cached, "data not cached")
728+         data_cached  =  self .ticker .recommendations_summary 
729+         self .assertIs (data , data_cached , "data not cached" )
684730
685-     def  test_recommendations_history (self ):   # alias for upgrades_downgrades 
731+     def  test_upgrades_downgrades (self ):
686732        data  =  self .ticker .upgrades_downgrades 
687-         data_history  =  self .ticker .recommendations_history 
688-         self .assertTrue (data .equals (data_history ))
689733        self .assertIsInstance (data , pd .DataFrame , "data has wrong type" )
690734        self .assertFalse (data .empty , "data is empty" )
691735        self .assertTrue (len (data .columns ) ==  4 , "data has wrong number of columns" )
@@ -695,6 +739,8 @@ def test_recommendations_history(self):  # alias for upgrades_downgrades
695739        data_cached  =  self .ticker .upgrades_downgrades 
696740        self .assertIs (data , data_cached , "data not cached" )
697741
742+     # Below will fail because not ported to Yahoo API 
743+ 
698744    # def test_analyst_price_target(self): 
699745    #     data = self.ticker.analyst_price_target 
700746    #     self.assertIsInstance(data, pd.DataFrame, "data has wrong type") 
@@ -711,28 +757,6 @@ def test_recommendations_history(self):  # alias for upgrades_downgrades
711757    #     data_cached = self.ticker.revenue_forecasts 
712758    #     self.assertIs(data, data_cached, "data not cached") 
713759
714-     def  test_calendar (self ):
715-         data  =  self .ticker .calendar 
716-         self .assertIsInstance (data , dict , "data has wrong type" )
717-         self .assertTrue (len (data ) >  0 , "data is empty" )
718-         self .assertIn ("Earnings Date" , data .keys (), "data missing expected key" )
719-         self .assertIn ("Earnings Average" , data .keys (), "data missing expected key" )
720-         self .assertIn ("Earnings Low" , data .keys (), "data missing expected key" )
721-         self .assertIn ("Earnings High" , data .keys (), "data missing expected key" )
722-         self .assertIn ("Revenue Average" , data .keys (), "data missing expected key" )
723-         self .assertIn ("Revenue Low" , data .keys (), "data missing expected key" )
724-         self .assertIn ("Revenue High" , data .keys (), "data missing expected key" )
725-         # dividend date is not available for tested ticker GOOGL 
726-         if  self .ticker .ticker  !=  "GOOGL" :
727-             self .assertIn ("Dividend Date" , data .keys (), "data missing expected key" )
728-         # ex-dividend date is not always available 
729-         data_cached  =  self .ticker .calendar 
730-         self .assertIs (data , data_cached , "data not cached" )
731- 
732-     # def test_shares(self): 
733-     #     data = self.ticker.shares 
734-     #     self.assertIsInstance(data, pd.DataFrame, "data has wrong type") 
735-     #     self.assertFalse(data.empty, "data is empty") 
736760
737761
738762class  TestTickerInfo (unittest .TestCase ):
@@ -777,11 +801,11 @@ def test_complementary_info(self):
777801
778802        # We don't expect this one to have a trailing PEG ratio 
779803        data1  =  self .tickers [0 ].info 
780-         self .assertEqual (data1 ['trailingPegRatio' ],  None )
804+         self .assertIsNone (data1 ['trailingPegRatio' ])
781805
782806        # This one should have a trailing PEG ratio 
783807        data2  =  self .tickers [2 ].info 
784-         self .assertEqual (data2 ['trailingPegRatio' ], 1.2713 )
808+         self .assertIsInstance (data2 ['trailingPegRatio' ], float )
785809        pass 
786810
787811    # def test_fast_info_matches_info(self): 
0 commit comments