Annotate api/zulip/__init__.py.
Note that we still can't run mypy against this file and other files, because of how the interface is dynamically created via _register. We will need to change that or use a stub file to make it possible to annotate this. This was tweaked by tabbott to fix some bugs.
This commit is contained in:
		
							parent
							
								
									2e2b8af9fd
								
							
						
					
					
						commit
						25a8315f71
					
				
					 1 changed files with 41 additions and 17 deletions
				
			
		|  | @ -38,8 +38,7 @@ from six.moves.configparser import SafeConfigParser | ||||||
| from six.moves import urllib | from six.moves import urllib | ||||||
| import logging | import logging | ||||||
| import six | import six | ||||||
| from typing import Any, Dict | from typing import Any, Callable, Dict, Mapping, Optional, Tuple, Union | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| __version__ = "0.2.5" | __version__ = "0.2.5" | ||||||
| 
 | 
 | ||||||
|  | @ -47,7 +46,7 @@ logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| # Check that we have a recent enough version | # Check that we have a recent enough version | ||||||
| # Older versions don't provide the 'json' attribute on responses. | # Older versions don't provide the 'json' attribute on responses. | ||||||
| assert(LooseVersion(requests.__version__) >= LooseVersion('0.12.1')) # type: ignore # https://github.com/python/mypy/issues/1165 and https://github.com/python/typeshed/pull/206 | assert(LooseVersion(requests.__version__) >= LooseVersion('0.12.1')) | ||||||
| # In newer versions, the 'json' attribute is a function, not a property | # In newer versions, the 'json' attribute is a function, not a property | ||||||
| requests_json_is_function = callable(requests.Response.json) | requests_json_is_function = callable(requests.Response.json) | ||||||
| 
 | 
 | ||||||
|  | @ -55,26 +54,31 @@ API_VERSTRING = "v1/" | ||||||
| 
 | 
 | ||||||
| class CountingBackoff(object): | class CountingBackoff(object): | ||||||
|     def __init__(self, maximum_retries=10, timeout_success_equivalent=None): |     def __init__(self, maximum_retries=10, timeout_success_equivalent=None): | ||||||
|  |         # type: (int, Optional[float]) -> None | ||||||
|         self.number_of_retries = 0 |         self.number_of_retries = 0 | ||||||
|         self.maximum_retries = maximum_retries |         self.maximum_retries = maximum_retries | ||||||
|         self.timeout_success_equivalent = timeout_success_equivalent |         self.timeout_success_equivalent = timeout_success_equivalent | ||||||
|         self.last_attempt_time = 0 |         self.last_attempt_time = 0.0 | ||||||
| 
 | 
 | ||||||
|     def keep_going(self): |     def keep_going(self): | ||||||
|  |         # type: () -> bool | ||||||
|         self._check_success_timeout() |         self._check_success_timeout() | ||||||
|         return self.number_of_retries < self.maximum_retries |         return self.number_of_retries < self.maximum_retries | ||||||
| 
 | 
 | ||||||
|     def succeed(self): |     def succeed(self): | ||||||
|  |         # type: () -> None | ||||||
|         self.number_of_retries = 0 |         self.number_of_retries = 0 | ||||||
|         self.last_attempt_time = time.time() |         self.last_attempt_time = time.time() | ||||||
| 
 | 
 | ||||||
|     def fail(self): |     def fail(self): | ||||||
|  |         # type: () -> None | ||||||
|         self._check_success_timeout() |         self._check_success_timeout() | ||||||
|         self.number_of_retries = min(self.number_of_retries + 1, |         self.number_of_retries = min(self.number_of_retries + 1, | ||||||
|                                      self.maximum_retries) |                                      self.maximum_retries) | ||||||
|         self.last_attempt_time = time.time() |         self.last_attempt_time = time.time() | ||||||
| 
 | 
 | ||||||
|     def _check_success_timeout(self): |     def _check_success_timeout(self): | ||||||
|  |         # type: () -> None | ||||||
|         if (self.timeout_success_equivalent is not None |         if (self.timeout_success_equivalent is not None | ||||||
|             and self.last_attempt_time != 0 |             and self.last_attempt_time != 0 | ||||||
|             and time.time() - self.last_attempt_time > self.timeout_success_equivalent): |             and time.time() - self.last_attempt_time > self.timeout_success_equivalent): | ||||||
|  | @ -82,6 +86,7 @@ class CountingBackoff(object): | ||||||
| 
 | 
 | ||||||
| class RandomExponentialBackoff(CountingBackoff): | class RandomExponentialBackoff(CountingBackoff): | ||||||
|     def fail(self): |     def fail(self): | ||||||
|  |         # type: () -> None | ||||||
|         super(RandomExponentialBackoff, self).fail() |         super(RandomExponentialBackoff, self).fail() | ||||||
|         # Exponential growth with ratio sqrt(2); compute random delay |         # Exponential growth with ratio sqrt(2); compute random delay | ||||||
|         # between x and 2x where x is growing exponentially |         # between x and 2x where x is growing exponentially | ||||||
|  | @ -95,9 +100,11 @@ class RandomExponentialBackoff(CountingBackoff): | ||||||
|         time.sleep(delay) |         time.sleep(delay) | ||||||
| 
 | 
 | ||||||
| def _default_client(): | def _default_client(): | ||||||
|  |     # type: () -> str | ||||||
|     return "ZulipPython/" + __version__ |     return "ZulipPython/" + __version__ | ||||||
| 
 | 
 | ||||||
| def generate_option_group(parser, prefix=''): | def generate_option_group(parser, prefix=''): | ||||||
|  |     # type: (optparse.OptionParser, str) ->  optparse.OptionGroup | ||||||
|     group = optparse.OptionGroup(parser, 'Zulip API configuration') |     group = optparse.OptionGroup(parser, 'Zulip API configuration') | ||||||
|     group.add_option('--%ssite' % (prefix,), |     group.add_option('--%ssite' % (prefix,), | ||||||
|                      dest="zulip_site", |                      dest="zulip_site", | ||||||
|  | @ -148,6 +155,7 @@ def generate_option_group(parser, prefix=''): | ||||||
|     return group |     return group | ||||||
| 
 | 
 | ||||||
| def init_from_options(options, client=None): | def init_from_options(options, client=None): | ||||||
|  |     # type: (Any, Optional[str]) -> Client | ||||||
|     if options.zulip_client is not None: |     if options.zulip_client is not None: | ||||||
|         client = options.zulip_client |         client = options.zulip_client | ||||||
|     elif client is None: |     elif client is None: | ||||||
|  | @ -160,6 +168,7 @@ def init_from_options(options, client=None): | ||||||
|                   client_cert_key=options.client_cert_key) |                   client_cert_key=options.client_cert_key) | ||||||
| 
 | 
 | ||||||
| def get_default_config_filename(): | def get_default_config_filename(): | ||||||
|  |     # type: () -> str | ||||||
|     config_file = os.path.join(os.environ["HOME"], ".zuliprc") |     config_file = os.path.join(os.environ["HOME"], ".zuliprc") | ||||||
|     if (not os.path.exists(config_file) and |     if (not os.path.exists(config_file) and | ||||||
|         os.path.exists(os.path.join(os.environ["HOME"], ".humbugrc"))): |         os.path.exists(os.path.join(os.environ["HOME"], ".humbugrc"))): | ||||||
|  | @ -173,6 +182,7 @@ class Client(object): | ||||||
|                  site=None, client=None, |                  site=None, client=None, | ||||||
|                  cert_bundle=None, insecure=None, |                  cert_bundle=None, insecure=None, | ||||||
|                  client_cert=None, client_cert_key=None): |                  client_cert=None, client_cert_key=None): | ||||||
|  |         # type: (Optional[str], Optional[str], Optional[str], bool, bool, Optional[str], Optional[str], Optional[str], bool, Optional[str], Optional[str]) -> None | ||||||
|         if client is None: |         if client is None: | ||||||
|             client = _default_client() |             client = _default_client() | ||||||
| 
 | 
 | ||||||
|  | @ -230,7 +240,7 @@ class Client(object): | ||||||
|         self.client_name = client |         self.client_name = client | ||||||
| 
 | 
 | ||||||
|         if insecure: |         if insecure: | ||||||
|             self.tls_verification = False |             self.tls_verification = False # type: Union[bool, str] | ||||||
|         elif cert_bundle is not None: |         elif cert_bundle is not None: | ||||||
|             if not os.path.isfile(cert_bundle): |             if not os.path.isfile(cert_bundle): | ||||||
|                 raise RuntimeError("tls bundle '%s' does not exist" |                 raise RuntimeError("tls bundle '%s' does not exist" | ||||||
|  | @ -256,6 +266,7 @@ class Client(object): | ||||||
|         self.client_cert_key = client_cert_key |         self.client_cert_key = client_cert_key | ||||||
| 
 | 
 | ||||||
|     def get_user_agent(self): |     def get_user_agent(self): | ||||||
|  |         # type: () -> str | ||||||
|         vendor = '' |         vendor = '' | ||||||
|         vendor_version = '' |         vendor_version = '' | ||||||
|         try: |         try: | ||||||
|  | @ -280,6 +291,7 @@ class Client(object): | ||||||
|                 ) |                 ) | ||||||
| 
 | 
 | ||||||
|     def do_api_query(self, orig_request, url, method="POST", longpolling = False): |     def do_api_query(self, orig_request, url, method="POST", longpolling = False): | ||||||
|  |         # type: (Mapping[str, Any], str, str, bool) -> Dict[str, Any] | ||||||
|         request = {} |         request = {} | ||||||
| 
 | 
 | ||||||
|         for (key, val) in six.iteritems(orig_request): |         for (key, val) in six.iteritems(orig_request): | ||||||
|  | @ -295,6 +307,7 @@ class Client(object): | ||||||
|         } # type: Dict[str, Any] |         } # type: Dict[str, Any] | ||||||
| 
 | 
 | ||||||
|         def error_retry(error_string): |         def error_retry(error_string): | ||||||
|  |             # type: (str) -> bool | ||||||
|             if not self.retry_on_errors or query_state["failures"] >= 10: |             if not self.retry_on_errors or query_state["failures"] >= 10: | ||||||
|                 return False |                 return False | ||||||
|             if self.verbose: |             if self.verbose: | ||||||
|  | @ -311,6 +324,7 @@ class Client(object): | ||||||
|             return True |             return True | ||||||
| 
 | 
 | ||||||
|         def end_error_retry(succeeded): |         def end_error_retry(succeeded): | ||||||
|  |             # type: (bool) -> None | ||||||
|             if query_state["had_error_retry"] and self.verbose: |             if query_state["had_error_retry"] and self.verbose: | ||||||
|                 if succeeded: |                 if succeeded: | ||||||
|                     print("Success!") |                     print("Success!") | ||||||
|  | @ -327,7 +341,7 @@ class Client(object): | ||||||
| 
 | 
 | ||||||
|                 # Build a client cert object for requests |                 # Build a client cert object for requests | ||||||
|                 if self.client_cert_key is not None: |                 if self.client_cert_key is not None: | ||||||
|                     client_cert = (self.client_cert, self.client_cert_key) |                     client_cert = (self.client_cert, self.client_cert_key) # type: Union[str, Tuple[str, str]] | ||||||
|                 else: |                 else: | ||||||
|                     client_cert = self.client_cert |                     client_cert = self.client_cert | ||||||
| 
 | 
 | ||||||
|  | @ -391,15 +405,15 @@ class Client(object): | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _register(cls, name, url=None, make_request=None, |     def _register(cls, name, url=None, make_request=None, | ||||||
|                   method="POST", computed_url=None, **query_kwargs): |                   method="POST", computed_url=None, **query_kwargs): | ||||||
|  |         # type: (Any, str, Optional[Callable], str, Optional[Callable], **Any) -> Any | ||||||
|         if url is None: |         if url is None: | ||||||
|             url = name |             url = name | ||||||
|         if make_request is None: |         if make_request is None: | ||||||
|             def make_request(request=None): |             def make_request(**kwargs): | ||||||
|                 if request is None: |                 # type: (**Any) -> Any | ||||||
|                     request = {} |                 return kwargs.get("request", {}) | ||||||
|                 return request |  | ||||||
| 
 |  | ||||||
|         def call(self, *args, **kwargs): |         def call(self, *args, **kwargs): | ||||||
|  |             # type: (*Any, **Any) -> str | ||||||
|             request = make_request(*args, **kwargs) |             request = make_request(*args, **kwargs) | ||||||
|             if computed_url is not None: |             if computed_url is not None: | ||||||
|                 req_url = computed_url(request) |                 req_url = computed_url(request) | ||||||
|  | @ -410,15 +424,16 @@ class Client(object): | ||||||
|         setattr(cls, name, call) |         setattr(cls, name, call) | ||||||
| 
 | 
 | ||||||
|     def call_on_each_event(self, callback, event_types=None, narrow=None): |     def call_on_each_event(self, callback, event_types=None, narrow=None): | ||||||
|  |         # type: (Callable, Optional[List[str]], Any) -> None | ||||||
|         if narrow is None: |         if narrow is None: | ||||||
|             narrow = [] |             narrow = [] | ||||||
| 
 |  | ||||||
|         def do_register(): |         def do_register(): | ||||||
|  |             # type: () -> Tuple[str, int] | ||||||
|             while True: |             while True: | ||||||
|                 if event_types is None: |                 if event_types is None: | ||||||
|                     res = self.register() |                     res = self.register() # type: ignore | ||||||
|                 else: |                 else: | ||||||
|                     res = self.register(event_types=event_types, narrow=narrow) |                     res = self.register(event_types=event_types, narrow=narrow) # type: ignore | ||||||
| 
 | 
 | ||||||
|                 if 'error' in res.get('result'): |                 if 'error' in res.get('result'): | ||||||
|                     if self.verbose: |                     if self.verbose: | ||||||
|  | @ -432,7 +447,7 @@ class Client(object): | ||||||
|             if queue_id is None: |             if queue_id is None: | ||||||
|                 (queue_id, last_event_id) = do_register() |                 (queue_id, last_event_id) = do_register() | ||||||
| 
 | 
 | ||||||
|             res = self.get_events(queue_id=queue_id, last_event_id=last_event_id) |             res = self.get_events(queue_id=queue_id, last_event_id=last_event_id) # type: ignore | ||||||
|             if 'error' in res.get('result'): |             if 'error' in res.get('result'): | ||||||
|                 if res["result"] == "http-error": |                 if res["result"] == "http-error": | ||||||
|                     if self.verbose: |                     if self.verbose: | ||||||
|  | @ -463,24 +478,29 @@ class Client(object): | ||||||
|                 callback(event) |                 callback(event) | ||||||
| 
 | 
 | ||||||
|     def call_on_each_message(self, callback): |     def call_on_each_message(self, callback): | ||||||
|  |         # type: (Callable) -> None | ||||||
|         def event_callback(event): |         def event_callback(event): | ||||||
|  |             # type: (Dict[str, str]) -> None | ||||||
|             if event['type'] == 'message': |             if event['type'] == 'message': | ||||||
|                 callback(event['message']) |                 callback(event['message']) | ||||||
| 
 |  | ||||||
|         self.call_on_each_event(event_callback, ['message']) |         self.call_on_each_event(event_callback, ['message']) | ||||||
| 
 | 
 | ||||||
| def _mk_subs(streams, **kwargs): | def _mk_subs(streams, **kwargs): | ||||||
|  |     # type: (str, **Any ) -> Dict[str, str] | ||||||
|     result = kwargs |     result = kwargs | ||||||
|     result['subscriptions'] = streams |     result['subscriptions'] = streams | ||||||
|     return result |     return result | ||||||
| 
 | 
 | ||||||
| def _mk_rm_subs(streams): | def _mk_rm_subs(streams): | ||||||
|  |     # type: (str) -> Dict[str, str] | ||||||
|     return {'delete': streams} |     return {'delete': streams} | ||||||
| 
 | 
 | ||||||
| def _mk_deregister(queue_id): | def _mk_deregister(queue_id): | ||||||
|  |     # type: (str) -> Dict[str, str] | ||||||
|     return {'queue_id': queue_id} |     return {'queue_id': queue_id} | ||||||
| 
 | 
 | ||||||
| def _mk_events(event_types=None, narrow=None): | def _mk_events(event_types=None, narrow=None): | ||||||
|  |     # type: (Any, List[None]) -> Dict[Any, List[None]] | ||||||
|     if event_types is None: |     if event_types is None: | ||||||
|         return dict() |         return dict() | ||||||
|     if narrow is None: |     if narrow is None: | ||||||
|  | @ -488,6 +508,7 @@ def _mk_events(event_types=None, narrow=None): | ||||||
|     return dict(event_types=event_types, narrow=narrow) |     return dict(event_types=event_types, narrow=narrow) | ||||||
| 
 | 
 | ||||||
| def _kwargs_to_dict(**kwargs): | def _kwargs_to_dict(**kwargs): | ||||||
|  |     # type: (**Any) -> Any | ||||||
|     return kwargs |     return kwargs | ||||||
| 
 | 
 | ||||||
| class ZulipStream(object): | class ZulipStream(object): | ||||||
|  | @ -496,19 +517,22 @@ class ZulipStream(object): | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     def __init__(self, type, to, subject, **kwargs): |     def __init__(self, type, to, subject, **kwargs): | ||||||
|  |         # type: (str, str, str,  **Any) -> None | ||||||
|         self.client = Client(**kwargs) |         self.client = Client(**kwargs) | ||||||
|         self.type = type |         self.type = type | ||||||
|         self.to = to |         self.to = to | ||||||
|         self.subject = subject |         self.subject = subject | ||||||
| 
 | 
 | ||||||
|     def write(self, content): |     def write(self, content): | ||||||
|  |         # type: (str) -> None | ||||||
|         message = {"type": self.type, |         message = {"type": self.type, | ||||||
|                    "to": self.to, |                    "to": self.to, | ||||||
|                    "subject": self.subject, |                    "subject": self.subject, | ||||||
|                    "content": content} |                    "content": content} | ||||||
|         self.client.send_message(message) |         self.client.send_message(message) # type: ignore | ||||||
| 
 | 
 | ||||||
|     def flush(self): |     def flush(self): | ||||||
|  |         # type: () -> None | ||||||
|         pass |         pass | ||||||
| 
 | 
 | ||||||
| Client._register('send_message', url='messages', make_request=(lambda request: request)) | Client._register('send_message', url='messages', make_request=(lambda request: request)) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Juan Verhook
						Juan Verhook