Spaces:
Running
Running
| # Ultralytics YOLO 🚀, AGPL-3.0 license | |
| import os | |
| import platform | |
| import random | |
| import sys | |
| import threading | |
| import time | |
| from pathlib import Path | |
| import requests | |
| from tqdm import tqdm | |
| from ultralytics.utils import (ENVIRONMENT, LOGGER, ONLINE, RANK, SETTINGS, TESTS_RUNNING, TQDM_BAR_FORMAT, TryExcept, | |
| __version__, colorstr, get_git_origin_url, is_colab, is_git_dir, is_pip_package) | |
| from ultralytics.utils.downloads import GITHUB_ASSET_NAMES | |
| PREFIX = colorstr('Ultralytics HUB: ') | |
| HELP_MSG = 'If this issue persists please visit https://github.com/ultralytics/hub/issues for assistance.' | |
| HUB_API_ROOT = os.environ.get('ULTRALYTICS_HUB_API', 'https://api.ultralytics.com') | |
| HUB_WEB_ROOT = os.environ.get('ULTRALYTICS_HUB_WEB', 'https://hub.ultralytics.com') | |
| def request_with_credentials(url: str) -> any: | |
| """ | |
| Make an AJAX request with cookies attached in a Google Colab environment. | |
| Args: | |
| url (str): The URL to make the request to. | |
| Returns: | |
| (any): The response data from the AJAX request. | |
| Raises: | |
| OSError: If the function is not run in a Google Colab environment. | |
| """ | |
| if not is_colab(): | |
| raise OSError('request_with_credentials() must run in a Colab environment') | |
| from google.colab import output # noqa | |
| from IPython import display # noqa | |
| display.display( | |
| display.Javascript(""" | |
| window._hub_tmp = new Promise((resolve, reject) => { | |
| const timeout = setTimeout(() => reject("Failed authenticating existing browser session"), 5000) | |
| fetch("%s", { | |
| method: 'POST', | |
| credentials: 'include' | |
| }) | |
| .then((response) => resolve(response.json())) | |
| .then((json) => { | |
| clearTimeout(timeout); | |
| }).catch((err) => { | |
| clearTimeout(timeout); | |
| reject(err); | |
| }); | |
| }); | |
| """ % url)) | |
| return output.eval_js('_hub_tmp') | |
| def requests_with_progress(method, url, **kwargs): | |
| """ | |
| Make an HTTP request using the specified method and URL, with an optional progress bar. | |
| Args: | |
| method (str): The HTTP method to use (e.g. 'GET', 'POST'). | |
| url (str): The URL to send the request to. | |
| **kwargs (dict): Additional keyword arguments to pass to the underlying `requests.request` function. | |
| Returns: | |
| (requests.Response): The response object from the HTTP request. | |
| Note: | |
| If 'progress' is set to True, the progress bar will display the download progress | |
| for responses with a known content length. | |
| """ | |
| progress = kwargs.pop('progress', False) | |
| if not progress: | |
| return requests.request(method, url, **kwargs) | |
| response = requests.request(method, url, stream=True, **kwargs) | |
| total = int(response.headers.get('content-length', 0)) # total size | |
| try: | |
| pbar = tqdm(total=total, unit='B', unit_scale=True, unit_divisor=1024, bar_format=TQDM_BAR_FORMAT) | |
| for data in response.iter_content(chunk_size=1024): | |
| pbar.update(len(data)) | |
| pbar.close() | |
| except requests.exceptions.ChunkedEncodingError: # avoid 'Connection broken: IncompleteRead' warnings | |
| response.close() | |
| return response | |
| def smart_request(method, url, retry=3, timeout=30, thread=True, code=-1, verbose=True, progress=False, **kwargs): | |
| """ | |
| Makes an HTTP request using the 'requests' library, with exponential backoff retries up to a specified timeout. | |
| Args: | |
| method (str): The HTTP method to use for the request. Choices are 'post' and 'get'. | |
| url (str): The URL to make the request to. | |
| retry (int, optional): Number of retries to attempt before giving up. Default is 3. | |
| timeout (int, optional): Timeout in seconds after which the function will give up retrying. Default is 30. | |
| thread (bool, optional): Whether to execute the request in a separate daemon thread. Default is True. | |
| code (int, optional): An identifier for the request, used for logging purposes. Default is -1. | |
| verbose (bool, optional): A flag to determine whether to print out to console or not. Default is True. | |
| progress (bool, optional): Whether to show a progress bar during the request. Default is False. | |
| **kwargs (dict): Keyword arguments to be passed to the requests function specified in method. | |
| Returns: | |
| (requests.Response): The HTTP response object. If the request is executed in a separate thread, returns None. | |
| """ | |
| retry_codes = (408, 500) # retry only these codes | |
| def func(func_method, func_url, **func_kwargs): | |
| """Make HTTP requests with retries and timeouts, with optional progress tracking.""" | |
| r = None # response | |
| t0 = time.time() # initial time for timer | |
| for i in range(retry + 1): | |
| if (time.time() - t0) > timeout: | |
| break | |
| r = requests_with_progress(func_method, func_url, **func_kwargs) # i.e. get(url, data, json, files) | |
| if r.status_code < 300: # return codes in the 2xx range are generally considered "good" or "successful" | |
| break | |
| try: | |
| m = r.json().get('message', 'No JSON message.') | |
| except AttributeError: | |
| m = 'Unable to read JSON.' | |
| if i == 0: | |
| if r.status_code in retry_codes: | |
| m += f' Retrying {retry}x for {timeout}s.' if retry else '' | |
| elif r.status_code == 429: # rate limit | |
| h = r.headers # response headers | |
| m = f"Rate limit reached ({h['X-RateLimit-Remaining']}/{h['X-RateLimit-Limit']}). " \ | |
| f"Please retry after {h['Retry-After']}s." | |
| if verbose: | |
| LOGGER.warning(f'{PREFIX}{m} {HELP_MSG} ({r.status_code} #{code})') | |
| if r.status_code not in retry_codes: | |
| return r | |
| time.sleep(2 ** i) # exponential standoff | |
| return r | |
| args = method, url | |
| kwargs['progress'] = progress | |
| if thread: | |
| threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True).start() | |
| else: | |
| return func(*args, **kwargs) | |
| class Events: | |
| """ | |
| A class for collecting anonymous event analytics. Event analytics are enabled when sync=True in settings and | |
| disabled when sync=False. Run 'yolo settings' to see and update settings YAML file. | |
| Attributes: | |
| url (str): The URL to send anonymous events. | |
| rate_limit (float): The rate limit in seconds for sending events. | |
| metadata (dict): A dictionary containing metadata about the environment. | |
| enabled (bool): A flag to enable or disable Events based on certain conditions. | |
| """ | |
| url = 'https://www.google-analytics.com/mp/collect?measurement_id=G-X8NCJYTQXM&api_secret=QLQrATrNSwGRFRLE-cbHJw' | |
| def __init__(self): | |
| """ | |
| Initializes the Events object with default values for events, rate_limit, and metadata. | |
| """ | |
| self.events = [] # events list | |
| self.rate_limit = 60.0 # rate limit (seconds) | |
| self.t = 0.0 # rate limit timer (seconds) | |
| self.metadata = { | |
| 'cli': Path(sys.argv[0]).name == 'yolo', | |
| 'install': 'git' if is_git_dir() else 'pip' if is_pip_package() else 'other', | |
| 'python': '.'.join(platform.python_version_tuple()[:2]), # i.e. 3.10 | |
| 'version': __version__, | |
| 'env': ENVIRONMENT, | |
| 'session_id': round(random.random() * 1E15), | |
| 'engagement_time_msec': 1000} | |
| self.enabled = \ | |
| SETTINGS['sync'] and \ | |
| RANK in (-1, 0) and \ | |
| not TESTS_RUNNING and \ | |
| ONLINE and \ | |
| (is_pip_package() or get_git_origin_url() == 'https://github.com/ultralytics/ultralytics.git') | |
| def __call__(self, cfg): | |
| """ | |
| Attempts to add a new event to the events list and send events if the rate limit is reached. | |
| Args: | |
| cfg (IterableSimpleNamespace): The configuration object containing mode and task information. | |
| """ | |
| if not self.enabled: | |
| # Events disabled, do nothing | |
| return | |
| # Attempt to add to events | |
| if len(self.events) < 25: # Events list limited to 25 events (drop any events past this) | |
| params = { | |
| **self.metadata, 'task': cfg.task, | |
| 'model': cfg.model if cfg.model in GITHUB_ASSET_NAMES else 'custom'} | |
| if cfg.mode == 'export': | |
| params['format'] = cfg.format | |
| self.events.append({'name': cfg.mode, 'params': params}) | |
| # Check rate limit | |
| t = time.time() | |
| if (t - self.t) < self.rate_limit: | |
| # Time is under rate limiter, wait to send | |
| return | |
| # Time is over rate limiter, send now | |
| data = {'client_id': SETTINGS['uuid'], 'events': self.events} # SHA-256 anonymized UUID hash and events list | |
| # POST equivalent to requests.post(self.url, json=data) | |
| smart_request('post', self.url, json=data, retry=0, verbose=False) | |
| # Reset events and rate limit timer | |
| self.events = [] | |
| self.t = t | |
| # Run below code on hub/utils init ------------------------------------------------------------------------------------- | |
| events = Events() | |