diff options
-rw-r--r-- | CHANGELOG.rst | 8 | ||||
-rw-r--r-- | docs/index.rst | 1 | ||||
-rw-r--r-- | mastodon/Mastodon.py | 64 | ||||
-rw-r--r-- | tests/test_streaming.py | 4 |
4 files changed, 59 insertions, 18 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5383275..f472941 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst | |||
@@ -2,6 +2,14 @@ A note on versioning: This librarys major version will grow with the APIs | |||
2 | version number. Breaking changes will be indicated by a change in the minor | 2 | version number. Breaking changes will be indicated by a change in the minor |
3 | (or major) version number, and will generally be avoided. | 3 | (or major) version number, and will generally be avoided. |
4 | 4 | ||
5 | v1.4.4 | ||
6 | ------ | ||
7 | * Added streaming_health | ||
8 | * Added support for local hashtag streams | ||
9 | * Made blurhash an optional dependency (Thanks limburgher) | ||
10 | * Fixed some things related to error handling (Thanks lefherz) | ||
11 | * Fixed various small documentation issues (Thanks lefherz) | ||
12 | |||
5 | v1.4.3 | 13 | v1.4.3 |
6 | ------ | 14 | ------ |
7 | * BREAKING BUT ONLY FOR YOUR DEPLOY, POTENTIALLY: http_ece and cryptography are now optional dependencies, if you need full webpush crypto support add the "webpush" feature to your Mastodon.py requirements or require one or both manually in your own setup.py. | 15 | * BREAKING BUT ONLY FOR YOUR DEPLOY, POTENTIALLY: http_ece and cryptography are now optional dependencies, if you need full webpush crypto support add the "webpush" feature to your Mastodon.py requirements or require one or both manually in your own setup.py. |
diff --git a/docs/index.rst b/docs/index.rst index 898c09a..1851b04 100644 --- a/docs/index.rst +++ b/docs/index.rst | |||
@@ -1109,6 +1109,7 @@ various exceptions: `MastodonMalformedEventError` if a received event cannot be | |||
1109 | .. automethod:: Mastodon.stream_local | 1109 | .. automethod:: Mastodon.stream_local |
1110 | .. automethod:: Mastodon.stream_hashtag | 1110 | .. automethod:: Mastodon.stream_hashtag |
1111 | .. automethod:: Mastodon.stream_list | 1111 | .. automethod:: Mastodon.stream_list |
1112 | .. automethod:: Mastodon.stream_healthy | ||
1112 | 1113 | ||
1113 | StreamListener | 1114 | StreamListener |
1114 | ~~~~~~~~~~~~~~ | 1115 | ~~~~~~~~~~~~~~ |
diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index ca38708..4f51d0d 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py | |||
@@ -2534,6 +2534,16 @@ class Mastodon: | |||
2534 | """ | 2534 | """ |
2535 | return self.__stream('/api/v1/streaming/direct', listener, run_async=run_async, timeout=timeout, reconnect_async=reconnect_async, reconnect_async_wait_sec=reconnect_async_wait_sec) | 2535 | return self.__stream('/api/v1/streaming/direct', listener, run_async=run_async, timeout=timeout, reconnect_async=reconnect_async, reconnect_async_wait_sec=reconnect_async_wait_sec) |
2536 | 2536 | ||
2537 | @api_version("2.5.0", "2.5.0", "2.5.0") | ||
2538 | def stream_healthy(self): | ||
2539 | """ | ||
2540 | Returns without True if streaming API is okay, False or raises an error otherwise. | ||
2541 | """ | ||
2542 | api_okay = self.__api_request('GET', '/api/v1/streaming/health', base_url_override = self.__get_streaming_base(), parse=False) | ||
2543 | if api_okay == b'OK': | ||
2544 | return True | ||
2545 | return False | ||
2546 | |||
2537 | ### | 2547 | ### |
2538 | # Internal helpers, dragons probably | 2548 | # Internal helpers, dragons probably |
2539 | ### | 2549 | ### |
@@ -2641,12 +2651,13 @@ class Mastodon: | |||
2641 | isotime = isotime[:-2] + ":" + isotime[-2:] | 2651 | isotime = isotime[:-2] + ":" + isotime[-2:] |
2642 | return isotime | 2652 | return isotime |
2643 | 2653 | ||
2644 | def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, do_ratelimiting=True, use_json = False): | 2654 | def __api_request(self, method, endpoint, params={}, files={}, headers={}, access_token_override=None, base_url_override=None, do_ratelimiting=True, use_json=False, parse=True): |
2645 | """ | 2655 | """ |
2646 | Internal API request helper. | 2656 | Internal API request helper. |
2647 | """ | 2657 | """ |
2648 | response = None | 2658 | response = None |
2649 | remaining_wait = 0 | 2659 | remaining_wait = 0 |
2660 | |||
2650 | # "pace" mode ratelimiting: Assume constant rate of requests, sleep a little less long than it | 2661 | # "pace" mode ratelimiting: Assume constant rate of requests, sleep a little less long than it |
2651 | # would take to not hit the rate limit at that request rate. | 2662 | # would take to not hit the rate limit at that request rate. |
2652 | if do_ratelimiting and self.ratelimit_method == "pace": | 2663 | if do_ratelimiting and self.ratelimit_method == "pace": |
@@ -2673,8 +2684,13 @@ class Mastodon: | |||
2673 | if not access_token_override is None: | 2684 | if not access_token_override is None: |
2674 | headers['Authorization'] = 'Bearer ' + access_token_override | 2685 | headers['Authorization'] = 'Bearer ' + access_token_override |
2675 | 2686 | ||
2687 | # Determine base URL | ||
2688 | base_url = self.api_base_url | ||
2689 | if not base_url_override is None: | ||
2690 | base_url = base_url_override | ||
2691 | |||
2676 | if self.debug_requests: | 2692 | if self.debug_requests: |
2677 | print('Mastodon: Request to endpoint "' + endpoint + '" using method "' + method + '".') | 2693 | print('Mastodon: Request to endpoint "' + base_url + endpoint + '" using method "' + method + '".') |
2678 | print('Parameters: ' + str(params)) | 2694 | print('Parameters: ' + str(params)) |
2679 | print('Headers: ' + str(headers)) | 2695 | print('Headers: ' + str(headers)) |
2680 | print('Files: ' + str(files)) | 2696 | print('Files: ' + str(files)) |
@@ -2695,9 +2711,9 @@ class Mastodon: | |||
2695 | kwargs['data'] = params | 2711 | kwargs['data'] = params |
2696 | else: | 2712 | else: |
2697 | kwargs['json'] = params | 2713 | kwargs['json'] = params |
2698 | 2714 | ||
2699 | response_object = self.session.request( | 2715 | response_object = self.session.request( |
2700 | method, self.api_base_url + endpoint, **kwargs) | 2716 | method, base_url + endpoint, **kwargs) |
2701 | except Exception as e: | 2717 | except Exception as e: |
2702 | raise MastodonNetworkError("Could not complete request: %s" % e) | 2718 | raise MastodonNetworkError("Could not complete request: %s" % e) |
2703 | 2719 | ||
@@ -2783,14 +2799,17 @@ class Mastodon: | |||
2783 | response_object.reason, | 2799 | response_object.reason, |
2784 | error_msg) | 2800 | error_msg) |
2785 | 2801 | ||
2786 | try: | 2802 | if parse == True: |
2787 | response = response_object.json(object_hook=self.__json_hooks) | 2803 | try: |
2788 | except: | 2804 | response = response_object.json(object_hook=self.__json_hooks) |
2789 | raise MastodonAPIError( | 2805 | except: |
2790 | "Could not parse response as JSON, response code was %s, " | 2806 | raise MastodonAPIError( |
2791 | "bad json content was '%s'" % (response_object.status_code, | 2807 | "Could not parse response as JSON, response code was %s, " |
2792 | response_object.content)) | 2808 | "bad json content was '%s'" % (response_object.status_code, |
2793 | 2809 | response_object.content)) | |
2810 | else: | ||
2811 | response = response_object.content | ||
2812 | |||
2794 | # Parse link headers | 2813 | # Parse link headers |
2795 | if isinstance(response, list) and \ | 2814 | if isinstance(response, list) and \ |
2796 | 'Link' in response_object.headers and \ | 2815 | 'Link' in response_object.headers and \ |
@@ -2857,15 +2876,12 @@ class Mastodon: | |||
2857 | 2876 | ||
2858 | return response | 2877 | return response |
2859 | 2878 | ||
2860 | def __stream(self, endpoint, listener, params={}, run_async=False, timeout=__DEFAULT_STREAM_TIMEOUT, reconnect_async=False, reconnect_async_wait_sec=__DEFAULT_STREAM_RECONNECT_WAIT_SEC): | 2879 | def __get_streaming_base(self): |
2861 | """ | 2880 | """ |
2862 | Internal streaming API helper. | 2881 | Internal streaming API helper. |
2863 | 2882 | ||
2864 | Returns a handle to the open connection that the user can close if they | 2883 | Returns the correct URL for the streaming API. |
2865 | wish to terminate it. | ||
2866 | """ | 2884 | """ |
2867 | |||
2868 | # Check if we have to redirect | ||
2869 | instance = self.instance() | 2885 | instance = self.instance() |
2870 | if "streaming_api" in instance["urls"] and instance["urls"]["streaming_api"] != self.api_base_url: | 2886 | if "streaming_api" in instance["urls"] and instance["urls"]["streaming_api"] != self.api_base_url: |
2871 | # This is probably a websockets URL, which is really for the browser, but requests can't handle it | 2887 | # This is probably a websockets URL, which is really for the browser, but requests can't handle it |
@@ -2881,6 +2897,18 @@ class Mastodon: | |||
2881 | instance["urls"]["streaming_api"])) | 2897 | instance["urls"]["streaming_api"])) |
2882 | else: | 2898 | else: |
2883 | url = self.api_base_url | 2899 | url = self.api_base_url |
2900 | return url | ||
2901 | |||
2902 | def __stream(self, endpoint, listener, params={}, run_async=False, timeout=__DEFAULT_STREAM_TIMEOUT, reconnect_async=False, reconnect_async_wait_sec=__DEFAULT_STREAM_RECONNECT_WAIT_SEC): | ||
2903 | """ | ||
2904 | Internal streaming API helper. | ||
2905 | |||
2906 | Returns a handle to the open connection that the user can close if they | ||
2907 | wish to terminate it. | ||
2908 | """ | ||
2909 | |||
2910 | # Check if we have to redirect | ||
2911 | url = self.__get_streaming_base() | ||
2884 | 2912 | ||
2885 | # The streaming server can't handle two slashes in a path, so remove trailing slashes | 2913 | # The streaming server can't handle two slashes in a path, so remove trailing slashes |
2886 | if url[-1] == '/': | 2914 | if url[-1] == '/': |
diff --git a/tests/test_streaming.py b/tests/test_streaming.py index 2e52f35..a471c41 100644 --- a/tests/test_streaming.py +++ b/tests/test_streaming.py | |||
@@ -365,3 +365,7 @@ def test_stream_user_local(api, api2): | |||
365 | assert updates[0].id == posted[0].id | 365 | assert updates[0].id == posted[0].id |
366 | 366 | ||
367 | t.join() | 367 | t.join() |
368 | |||
369 | @pytest.mark.vcr() | ||
370 | def test_stream_healthy(api_anonymous): | ||
371 | assert api_anonymous.stream_healthy() | ||