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