mirror of
https://github.com/wassname/ray.git
synced 2026-06-28 14:48:54 +08:00
119 lines
3.0 KiB
Python
119 lines
3.0 KiB
Python
import json
|
|
import logging
|
|
import random
|
|
import string
|
|
import time
|
|
import io
|
|
import os
|
|
|
|
import requests
|
|
from pygments import formatters, highlight, lexers
|
|
from ray.serve.context import FakeFlaskRequest, TaskContext
|
|
from ray.serve.http_util import build_flask_request
|
|
import itertools
|
|
import numpy as np
|
|
|
|
try:
|
|
import pydantic
|
|
except ImportError:
|
|
pydantic = None
|
|
|
|
|
|
def expand(l):
|
|
"""
|
|
Implements a nested flattening of a list.
|
|
Example:
|
|
>>> serve.utils.expand([1,2,[3,4,5],6])
|
|
[1,2,3,4,5,6]
|
|
>>> serve.utils.expand(["a", ["b", "c"], "d", ["e", "f"]])
|
|
["a", "b", "c", "d", "e", "f"]
|
|
"""
|
|
return list(
|
|
itertools.chain.from_iterable(
|
|
[x if isinstance(x, list) else [x] for x in l]))
|
|
|
|
|
|
def parse_request_item(request_item):
|
|
if request_item.request_context == TaskContext.Web:
|
|
is_web_context = True
|
|
asgi_scope, body_bytes = request_item.request_args
|
|
|
|
flask_request = build_flask_request(asgi_scope, io.BytesIO(body_bytes))
|
|
args = (flask_request, )
|
|
kwargs = {}
|
|
else:
|
|
is_web_context = False
|
|
args = (FakeFlaskRequest(), )
|
|
kwargs = request_item.request_kwargs
|
|
|
|
return args, kwargs, is_web_context
|
|
|
|
|
|
def _get_logger():
|
|
logger = logging.getLogger("ray.serve")
|
|
# TODO(simon): Make logging level configurable.
|
|
if os.environ.get("SERVE_LOG_DEBUG"):
|
|
logger.setLevel(logging.DEBUG)
|
|
else:
|
|
logger.setLevel(logging.INFO)
|
|
return logger
|
|
|
|
|
|
logger = _get_logger()
|
|
|
|
|
|
class ServeEncoder(json.JSONEncoder):
|
|
"""Ray.Serve's utility JSON encoder. Adds support for:
|
|
- bytes
|
|
- Pydantic types
|
|
- Exceptions
|
|
- numpy.ndarray
|
|
"""
|
|
|
|
def default(self, o): # pylint: disable=E0202
|
|
if isinstance(o, bytes):
|
|
return o.decode("utf-8")
|
|
if pydantic is not None and isinstance(o, pydantic.BaseModel):
|
|
return o.dict()
|
|
if isinstance(o, Exception):
|
|
return str(o)
|
|
if isinstance(o, np.ndarray):
|
|
return o.tolist()
|
|
return super().default(o)
|
|
|
|
|
|
def pformat_color_json(d):
|
|
"""Use pygments to pretty format and colroize dictionary"""
|
|
formatted_json = json.dumps(d, sort_keys=True, indent=4)
|
|
|
|
colorful_json = highlight(formatted_json, lexers.JsonLexer(),
|
|
formatters.TerminalFormatter())
|
|
|
|
return colorful_json
|
|
|
|
|
|
def block_until_http_ready(http_endpoint, num_retries=5, backoff_time_s=1):
|
|
http_is_ready = False
|
|
retries = num_retries
|
|
|
|
while not http_is_ready:
|
|
try:
|
|
resp = requests.get(http_endpoint)
|
|
assert resp.status_code == 200
|
|
http_is_ready = True
|
|
except Exception:
|
|
pass
|
|
|
|
# Exponential backoff
|
|
time.sleep(backoff_time_s)
|
|
backoff_time_s *= 2
|
|
|
|
retries -= 1
|
|
if retries == 0:
|
|
raise Exception(
|
|
"HTTP proxy not ready after {} retries.".format(num_retries))
|
|
|
|
|
|
def get_random_letters(length=6):
|
|
return "".join(random.choices(string.ascii_letters, k=length))
|