| @@ -1,12 +1,12 @@ | |||
| set(glog_CXXFLAGS "-D_FORTIFY_SOURCE=2 -O2 ${SECURE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") | |||
| set(glog_CFLAGS "-D_FORTIFY_SOURCE=2 -O2") | |||
| if (ENABLE_GITEE) | |||
| if(ENABLE_GITEE) | |||
| set(REQ_URL "https://gitee.com/mirrors/glog/repository/archive/v0.4.0.tar.gz") | |||
| set(MD5 "22fe340ddc231e6c8e46bc295320f8ee") | |||
| else() | |||
| set(REQ_URL "https://github.com/google/glog/archive/v0.4.0.tar.gz") | |||
| set(MD5 "0daea8785e6df922d7887755c3d100d0") | |||
| endif () | |||
| endif() | |||
| mindspore_add_pkg(glog | |||
| VER 0.4.0 | |||
| LIBS glog | |||
| @@ -14,4 +14,4 @@ mindspore_add_pkg(glog | |||
| MD5 ${MD5} | |||
| CMAKE_OPTION -DBUILD_TESTING=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=ON -DWITH_GFLAGS=OFF) | |||
| include_directories(${glog_INC}) | |||
| add_library(mindspore::glog ALIAS glog::glog) | |||
| add_library(mindspore_serving::glog ALIAS glog::glog) | |||
| @@ -241,7 +241,7 @@ Status GrpcTensorHelper::CreateInstanceFromRequest(const proto::PredictRequest & | |||
| MethodSignature method_signature; | |||
| if (!servable_signature.GetMethodDeclare(request_spec->method_name, &method_signature)) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "Method " << method_name << " is not registed for servable " << servable_name; | |||
| << "Method " << method_name << " is not registered for servable " << servable_name; | |||
| } | |||
| if (request.instances_size() == 0) { | |||
| @@ -269,7 +269,7 @@ Status GrpcTensorHelper::CreateReplyFromInstances(const proto::PredictRequest &r | |||
| MethodSignature method_signature; | |||
| if (!servable_signature.GetMethodDeclare(method_name, &method_signature)) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "Method " << method_name << " is not registed for servable " << servable_name; | |||
| << "Method " << method_name << " is not registered for servable " << servable_name; | |||
| } | |||
| *reply->mutable_servable_spec() = request.servable_spec(); | |||
| @@ -305,7 +305,7 @@ Status GrpcTensorHelper::CreateReplyFromInstances(const proto::PredictRequest &r | |||
| auto &output_tensor = output_intance.data[i]; | |||
| auto &proto_tensor = (*proto_items)[method_signature.outputs[i]]; | |||
| ProtoTensor result_tensor(&proto_tensor); | |||
| result_tensor.assgin(*output_tensor); | |||
| result_tensor.assign(*output_tensor); | |||
| } | |||
| } | |||
| return SUCCESS; | |||
| @@ -356,9 +356,15 @@ Status GrpcTensorHelper::CheckRequestTensor(const proto::Tensor &tensor) { | |||
| } | |||
| } else { | |||
| size_t element_num = tensor_input.element_cnt(); | |||
| if (element_num == 0) { // shape dim invalid or element count > max element num | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Tensor check failed: input " | |||
| << " shape " << tensor_input.shape() << " invalid"; | |||
| bool zero_dim = false; | |||
| for (auto &shape_item : tensor_input.shape()) { | |||
| if (shape_item < 0 || zero_dim) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Tensor check failed: input " | |||
| << " shape " << tensor_input.shape() << " invalid"; | |||
| } | |||
| if (shape_item == 0) { | |||
| zero_dim = true; | |||
| } | |||
| } | |||
| if (tensor_input.data_type() == kMSI_Unknown || tensor_input.itemsize() == 0) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Tensor check failed: input " | |||
| @@ -63,9 +63,6 @@ class Status { | |||
| } | |||
| #define INFER_STATUS(code) mindspore::serving::Status(code) < mindspore::serving::LogStream() | |||
| #define ERROR_INFER_STATUS(status, type, msg) \ | |||
| MSI_LOG_ERROR << msg; \ | |||
| status = mindspore::serving::Status(type, msg) | |||
| #define INFER_STATUS_LOG_ERROR(code) mindspore::serving::Status(code) = MSILOG_NOIF(ERROR) | |||
| #define INFER_STATUS_LOG_WARNING(code) mindspore::serving::Status(code) = MSILOG_NOIF(WARNING) | |||
| @@ -68,7 +68,7 @@ size_t TensorBase::GetTypeSize(DataType type) { | |||
| return 0; | |||
| } | |||
| void TensorBase::assgin(const TensorBase &other) { | |||
| void TensorBase::assign(const TensorBase &other) { | |||
| if (is_bytes_val_data()) { | |||
| clear_bytes_data(); | |||
| } | |||
| @@ -51,7 +51,7 @@ enum DataType { | |||
| class TensorBase; | |||
| using TensorBasePtr = std::shared_ptr<TensorBase>; | |||
| class MS_API TensorBase { | |||
| class MS_API TensorBase : public std::enable_shared_from_this<TensorBase> { | |||
| public: | |||
| TensorBase() = default; | |||
| virtual ~TensorBase() = default; | |||
| @@ -84,7 +84,7 @@ class MS_API TensorBase { | |||
| // TensorBase(const TensorBase& other) = delete; | |||
| // TensorBase& operator=(const TensorBase& other) = delete; | |||
| void assgin(const TensorBase &other); | |||
| void assign(const TensorBase &other); | |||
| Status concat(const std::vector<TensorBasePtr> &inputs); | |||
| bool is_bytes_val_data() const { return data_type() == kMSI_Bytes || data_type() == kMSI_String; } | |||
| }; | |||
| @@ -120,18 +120,16 @@ size_t GetB64OriginSize(size_t target_len, size_t tail_size) { | |||
| size_t GetTailEqualSize(const std::string &str) { | |||
| size_t length = str.size(); | |||
| if (length == 0 || length % 4 != 0) { | |||
| if (length % 4 != 0) { | |||
| return UINT32_MAX; | |||
| } | |||
| size_t count = 0; | |||
| if (str.at(str.size() - 1) == '=') { | |||
| if (length >= 1 && str[length - 1] == '=') { | |||
| count++; | |||
| } | |||
| if (str.at(str.size() - 2) == '=') { | |||
| if (length >= 2 && str[length - 2] == '=') { | |||
| count++; | |||
| } | |||
| return count; | |||
| } | |||
| @@ -148,8 +148,9 @@ bool RestfulService::JsonMatchDataType(const json &js, DataType type) { | |||
| flag = true; | |||
| } | |||
| } else if (js.is_string()) { | |||
| // string value can express all kinds type | |||
| flag = true; | |||
| if (type == kMSI_String) { | |||
| flag = true; | |||
| } | |||
| } else if (js.is_boolean()) { | |||
| if (type == kMSI_Bool) { | |||
| flag = true; | |||
| @@ -162,12 +163,9 @@ bool RestfulService::JsonMatchDataType(const json &js, DataType type) { | |||
| std::vector<int64_t> RestfulService::GetObjShape(const json &js) { | |||
| std::vector<int64_t> shape; | |||
| auto it = js.find(kShape); | |||
| if (it == js.end()) { | |||
| shape.push_back(1); | |||
| } else { | |||
| if (it != js.end()) { | |||
| shape = GetSpecifiedShape(it.value()); | |||
| } | |||
| return shape; | |||
| } | |||
| @@ -330,17 +328,24 @@ Status RestfulService::CheckObj(const json &js) { | |||
| if (!value.is_array()) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json object, key is 'shape', value should be array type"; | |||
| } | |||
| if (value.empty()) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json object, key is 'shape', array value should no be empty"; | |||
| } | |||
| bool zero_dims_before = false; | |||
| for (auto it = value.begin(); it != value.end(); ++it) { | |||
| if (!(it->is_number())) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json object, key is 'shape', array value should be number"; | |||
| if (zero_dims_before) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "json object, key is 'shape', invalid shape value " << value.dump(); | |||
| } | |||
| if (!(it->is_number_unsigned())) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "json object, key is 'shape', array value should be unsigned integer"; | |||
| } | |||
| auto number = it->get<int32_t>(); | |||
| if (number <= 0) { | |||
| if (number < 0) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "json object, key is 'shape', number value should be positive number"; | |||
| << "json object, key is 'shape', number value should not be negative number, shape value: " | |||
| << value.dump(); | |||
| } | |||
| if (number == 0) { | |||
| zero_dims_before = true; | |||
| } | |||
| } | |||
| shape_count++; | |||
| @@ -365,28 +370,29 @@ Status RestfulService::CheckObj(const json &js) { | |||
| // 1. parse request common func | |||
| Status RestfulService::ParseItem(const json &value, ProtoTensor *const pb_tensor) { | |||
| Status status(SUCCESS); | |||
| std::vector<int64_t> scalar_shape = {}; | |||
| if (value.is_number_integer()) { | |||
| DataType type = kMSI_Int32; | |||
| pb_tensor->set_data_type(type); | |||
| pb_tensor->set_shape({1}); | |||
| pb_tensor->set_shape(scalar_shape); | |||
| pb_tensor->resize_data(pb_tensor->GetTypeSize(type)); | |||
| status = GetScalarByType(type, value, 0, pb_tensor); | |||
| } else if (value.is_number_float()) { | |||
| DataType type = kMSI_Float32; | |||
| pb_tensor->set_data_type(type); | |||
| pb_tensor->set_shape({1}); | |||
| pb_tensor->set_shape(scalar_shape); | |||
| pb_tensor->resize_data(pb_tensor->GetTypeSize(type)); | |||
| status = GetScalarByType(type, value, 0, pb_tensor); | |||
| } else if (value.is_boolean()) { | |||
| DataType type = kMSI_Bool; | |||
| pb_tensor->set_data_type(type); | |||
| pb_tensor->set_shape({1}); | |||
| pb_tensor->set_shape(scalar_shape); | |||
| pb_tensor->resize_data(pb_tensor->GetTypeSize(type)); | |||
| status = GetScalarByType(type, value, 0, pb_tensor); | |||
| } else if (value.is_string()) { | |||
| DataType type = kMSI_String; | |||
| pb_tensor->set_data_type(type); | |||
| pb_tensor->set_shape({1}); | |||
| pb_tensor->set_shape(scalar_shape); | |||
| status = GetScalarByType(type, value, 0, pb_tensor); | |||
| } else if (value.is_object()) { | |||
| status = CheckObj(value); | |||
| @@ -400,10 +406,6 @@ Status RestfulService::ParseItem(const json &value, ProtoTensor *const pb_tensor | |||
| } | |||
| std::vector<int64_t> shape = GetObjShape(value); | |||
| if (shape.empty()) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "object json, shape is empty"; | |||
| } | |||
| bool is_tensor = false; | |||
| if (type != kMSI_String && type != kMSI_Bytes) { | |||
| is_tensor = true; | |||
| @@ -437,7 +439,7 @@ Status RestfulService::ParseItem(const json &value, ProtoTensor *const pb_tensor | |||
| is_tensor = true; | |||
| } | |||
| // intances mode:only support one item | |||
| // instances mode:only support one item | |||
| if (request_type_ == kInstanceType) { | |||
| if (!is_tensor) { | |||
| size_t elements_nums = std::accumulate(shape.begin(), shape.end(), 1LL, std::multiplies<size_t>()); | |||
| @@ -481,15 +483,15 @@ Status RestfulService::RecursiveGetArray(const json &json_data, size_t depth, si | |||
| std::vector<int64_t> required_shape = request_tensor->shape(); | |||
| if (depth >= required_shape.size()) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "current depth:" << depth << " is more than shape dims:" << required_shape.size(); | |||
| << "invalid json array: current depth " << depth << " is more than shape dims " << required_shape.size(); | |||
| } | |||
| if (!json_data.is_array()) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json type is not array"; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "invalid json array: json type is not array"; | |||
| } | |||
| if (json_data.size() != static_cast<size_t>(required_shape[depth])) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "json size is:" << json_data.size() << "; the " << depth << " dim need" | |||
| << " shape size:" << required_shape[depth]; | |||
| << "invalid json array: json size is " << json_data.size() << ", the dim " << depth << " expected to be " | |||
| << required_shape[depth]; | |||
| } | |||
| if (depth + 1 < required_shape.size()) { | |||
| size_t sub_element_cnt = | |||
| @@ -618,9 +620,7 @@ Status RestfulService::GetScalarData(const json &js, size_t index, bool is_bytes | |||
| auto value = js.get<std::string>(); | |||
| if (is_bytes) { | |||
| if (value.empty()) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "string value is empty"; | |||
| } | |||
| DataType real_type = request_tensor->data_type(); | |||
| auto tail_equal_size = GetTailEqualSize(value); | |||
| if (tail_equal_size == UINT32_MAX) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "'" << value << "' is illegal b64 encode string"; | |||
| @@ -631,7 +631,6 @@ Status RestfulService::GetScalarData(const json &js, size_t index, bool is_bytes | |||
| if (target_size != origin_size) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "decode base64 failed, size is not matched."; | |||
| } | |||
| DataType real_type = request_tensor->data_type(); | |||
| if (real_type == kMSI_Bytes || real_type == kMSI_String) { | |||
| request_tensor->add_bytes_data(buffer.data(), origin_size); | |||
| } else { | |||
| @@ -643,9 +642,10 @@ Status RestfulService::GetScalarData(const json &js, size_t index, bool is_bytes | |||
| << "; Given info: type:" << GetStringByDataType(real_type) << "; type size:" << type_size | |||
| << "; element nums:" << element_cnt; | |||
| } | |||
| auto data = reinterpret_cast<T *>(request_tensor->mutable_data()) + index; | |||
| memcpy_s(data, origin_size, buffer.data(), buffer.size()); | |||
| if (origin_size > 0) { | |||
| auto data = reinterpret_cast<T *>(request_tensor->mutable_data()) + index; | |||
| memcpy_s(data, origin_size, buffer.data(), buffer.size()); | |||
| } | |||
| } | |||
| } else { | |||
| request_tensor->add_bytes_data(reinterpret_cast<uint8_t *>(value.data()), value.length()); | |||
| @@ -680,7 +680,7 @@ Status RestfulService::RunRestful(const std::shared_ptr<RestfulRequest> &restful | |||
| MSI_TIME_STAMP_END(ParseRequest) | |||
| if (status != SUCCESS) { | |||
| std::string error_msg = status.StatusMessage(); | |||
| std::string msg = "Parser reqeust failed, " + error_msg; | |||
| std::string msg = "Parser request failed, " + error_msg; | |||
| status = msg; | |||
| return status; | |||
| } | |||
| @@ -730,7 +730,7 @@ Status RestfulService::ParseRequest(const std::shared_ptr<RestfulRequest> &restf | |||
| status = ParseInstancesMsg(js_msg, request); | |||
| break; | |||
| default: | |||
| return INFER_STATUS_LOG_ERROR(FAILED) << "restful reqeust only support instances mode"; | |||
| return INFER_STATUS_LOG_ERROR(FAILED) << "restful request only support instances mode"; | |||
| } | |||
| return status; | |||
| @@ -741,8 +741,7 @@ Status RestfulService::ParseReqCommonMsg(const std::shared_ptr<RestfulRequest> & | |||
| Status status(SUCCESS); | |||
| auto request_ptr = restful_request->decompose_event_request(); | |||
| if (request_ptr == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "Decompose event request is nullptr"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Decompose event request is nullptr"; | |||
| } | |||
| request->mutable_servable_spec()->set_name(request_ptr->model_name_); | |||
| request->mutable_servable_spec()->set_version_number(request_ptr->version_); | |||
| @@ -755,8 +754,7 @@ Status RestfulService::ParseInstancesMsg(const json &js_msg, PredictRequest *con | |||
| auto type = GetReqTypeStr(request_type_); | |||
| auto instances = js_msg.find(type); | |||
| if (instances == js_msg.end()) { | |||
| ERROR_INFER_STATUS(status, FAILED, "instances request json should have instances key word"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "instances request json should have instances key word"; | |||
| } | |||
| // get instances way:{key, value} or {value} | |||
| @@ -845,9 +843,7 @@ Status RestfulService::ParseReplyDetail(const proto::Tensor &tensor, json *const | |||
| Status status(SUCCESS); | |||
| const ProtoTensor pb_tensor(const_cast<proto::Tensor *>(&tensor)); | |||
| auto shape = pb_tensor.shape(); | |||
| size_t shape_size = std::accumulate(shape.begin(), shape.end(), 1LL, std::multiplies<size_t>()); | |||
| if (shape_size == 1) { | |||
| if (shape.empty()) { | |||
| status = ParseScalar(pb_tensor, 0, js); | |||
| if (status != SUCCESS) { | |||
| return status; | |||
| @@ -900,7 +896,7 @@ Status RestfulService::ParseScalar(const ProtoTensor &pb_tensor, size_t index, j | |||
| status = ParseScalarData<uint64_t>(pb_tensor, false, index, js); | |||
| break; | |||
| case kMSI_Float16: | |||
| ERROR_INFER_STATUS(status, FAILED, "fp16 reply is not supported"); | |||
| status = INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "fp16 reply is not supported"; | |||
| break; | |||
| case kMSI_Float32: | |||
| status = ParseScalarData<float>(pb_tensor, false, index, js); | |||
| @@ -915,7 +911,7 @@ Status RestfulService::ParseScalar(const ProtoTensor &pb_tensor, size_t index, j | |||
| status = ParseScalarData<std::string>(pb_tensor, true, index, js); | |||
| break; | |||
| default: | |||
| ERROR_INFER_STATUS(status, FAILED, "reply data type is not supported"); | |||
| status = INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "reply data type is not supported"; | |||
| break; | |||
| } | |||
| return status; | |||
| @@ -981,10 +977,15 @@ Status RestfulService::RecursiveParseArray(const ProtoTensor &pb_tensor, size_t | |||
| json *const out_json) { | |||
| Status status(SUCCESS); | |||
| std::vector<int64_t> required_shape = pb_tensor.shape(); | |||
| if (depth >= 10) { | |||
| return INFER_STATUS_LOG_ERROR(FAILED) << "result shape dims is larger than 10"; | |||
| if (depth >= required_shape.size()) { | |||
| return INFER_STATUS_LOG_ERROR(FAILED) | |||
| << "result shape dims is larger than result shape size " << required_shape.size(); | |||
| } | |||
| if (depth == required_shape.size() - 1) { | |||
| if (required_shape[depth] == 0) { // make empty array | |||
| out_json->push_back(json()); | |||
| out_json->clear(); | |||
| } | |||
| for (int i = 0; i < required_shape[depth]; i++) { | |||
| out_json->push_back(json()); | |||
| json &scalar_json = out_json->back(); | |||
| @@ -21,10 +21,12 @@ | |||
| #include <algorithm> | |||
| #include <utility> | |||
| static const char UrlKeyModel[] = "model"; | |||
| static const char UrlKeyVersion[] = "version"; | |||
| static const char UrlSplit[] = "/"; | |||
| static const char UrlKeyEnd[] = ":"; | |||
| namespace { | |||
| const char kUrlKeyModel[] = "model"; | |||
| const char kUrlKeyVersion[] = "version"; | |||
| const char kUrlSplit[] = "/"; | |||
| const char kUrlKeyEnd[] = ":"; | |||
| } // namespace | |||
| namespace mindspore { | |||
| namespace serving { | |||
| @@ -40,8 +42,8 @@ DecomposeEvRequest::~DecomposeEvRequest() { | |||
| std::string DecomposeEvRequest::UrlQuery(const std::string &url, const std::string &key) { | |||
| std::string::size_type start_pos(0); | |||
| if (key == UrlKeyEnd) { | |||
| if ((start_pos = url_.find(UrlKeyEnd)) != std::string::npos) { | |||
| if (key == kUrlKeyEnd) { | |||
| if ((start_pos = url_.find(kUrlKeyEnd)) != std::string::npos) { | |||
| return url_.substr(start_pos + 1, url_.size()); | |||
| } | |||
| } | |||
| @@ -49,10 +51,11 @@ std::string DecomposeEvRequest::UrlQuery(const std::string &url, const std::stri | |||
| int key_size = key.size() + 1; | |||
| std::string::size_type end_pos(0); | |||
| if ((start_pos = url.find(key)) != std::string::npos) { | |||
| end_pos = std::min(url.find(UrlSplit, start_pos + key_size), url.find(UrlKeyEnd, start_pos + key_size)); | |||
| if (end_pos != std::string::npos) { | |||
| return url.substr(start_pos + key_size, end_pos - start_pos - key_size); | |||
| end_pos = std::min(url.find(kUrlSplit, start_pos + key_size), url.find(kUrlKeyEnd, start_pos + key_size)); | |||
| if (end_pos == std::string::npos) { | |||
| return url.substr(start_pos + key_size); | |||
| } | |||
| return url.substr(start_pos + key_size, end_pos - start_pos - key_size); | |||
| } | |||
| return ""; | |||
| } | |||
| @@ -62,21 +65,17 @@ Status DecomposeEvRequest::GetPostMessageToJson() { | |||
| std::string message; | |||
| size_t input_size = evbuffer_get_length(event_request_->input_buffer); | |||
| if (input_size == 0) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "http message invalid"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "http message invalid"; | |||
| } else if (input_size > max_msg_size_) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "http message is bigger than " + std::to_string(max_msg_size_)); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "http message is bigger than " << max_msg_size_; | |||
| } else { | |||
| message.resize(input_size); | |||
| auto src_data = evbuffer_pullup(event_request_->input_buffer, -1); | |||
| if (src_data == nullptr) { | |||
| ERROR_INFER_STATUS(status, FAILED, "get http message failed."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "get http message failed."; | |||
| } | |||
| if (memcpy_s(message.data(), input_size, src_data, input_size) != EOK) { | |||
| ERROR_INFER_STATUS(status, FAILED, "copy http message failed."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "copy http message failed."; | |||
| } | |||
| } | |||
| MSI_TIME_STAMP_START(ParseJson) | |||
| @@ -84,9 +83,7 @@ Status DecomposeEvRequest::GetPostMessageToJson() { | |||
| request_message_ = nlohmann::json::parse(message); | |||
| } catch (nlohmann::json::exception &e) { | |||
| std::string json_exception = e.what(); | |||
| std::string error_message = "Illegal JSON format." + json_exception; | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, error_message); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Illegal JSON format." + json_exception; | |||
| } | |||
| MSI_TIME_STAMP_END(ParseJson) | |||
| @@ -100,8 +97,7 @@ Status DecomposeEvRequest::CheckRequestMethodValid() { | |||
| request_method_ = "POST"; | |||
| return status; | |||
| default: | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "http message only support POST right now"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "http message only support POST right now"; | |||
| } | |||
| } | |||
| @@ -120,35 +116,37 @@ Status DecomposeEvRequest::Decompose() { | |||
| // eg: /model/resnet/version/1:predict | |||
| url_ = evhttp_request_get_uri(event_request_); | |||
| if (url_.empty()) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "evhttp url is empty."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "evhttp url is empty."; | |||
| } | |||
| MSI_LOG_INFO << "url_: " << url_; | |||
| model_name_ = UrlQuery(url_, UrlKeyModel); | |||
| model_name_ = UrlQuery(url_, kUrlKeyModel); | |||
| if (model_name_.empty()) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[model] must contain."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "please check url, the keyword:[model] must contain."; | |||
| } | |||
| MSI_LOG_INFO << "model_name_: " << model_name_; | |||
| if (url_.find(UrlKeyVersion) != std::string::npos) { | |||
| auto version_str = UrlQuery(url_, UrlKeyVersion); | |||
| if (url_.find(kUrlKeyVersion) != std::string::npos) { | |||
| auto version_str = UrlQuery(url_, kUrlKeyVersion); | |||
| try { | |||
| version_ = std::stol(version_str); | |||
| auto version = std::stol(version_str); | |||
| if (version < 0 || version >= UINT32_MAX) { | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "please check url, version number range failed, request version number " << version_str; | |||
| } | |||
| version_ = static_cast<uint32_t>(version); | |||
| } catch (const std::invalid_argument &) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[version] must contain."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "please check url, the keyword:[version] value invalid, request version number " << version_str; | |||
| } catch (const std::out_of_range &) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[version] out of range."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) | |||
| << "please check url, version number range failed, request version number " << version_str; | |||
| } | |||
| MSI_LOG_INFO << "version_: " << version_; | |||
| } | |||
| service_method_ = UrlQuery(url_, UrlKeyEnd); | |||
| service_method_ = UrlQuery(url_, kUrlKeyEnd); | |||
| if (service_method_.empty()) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[service method] must contain."); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "please check url, the keyword:[service method] must contain."; | |||
| } | |||
| MSI_LOG_INFO << "service_method_: " << service_method_; | |||
| return status; | |||
| @@ -164,37 +162,31 @@ RestfulRequest::~RestfulRequest() { | |||
| } | |||
| Status RestfulRequest::RestfulReplayBufferInit() { | |||
| Status status(SUCCESS); | |||
| replay_buffer_ = evbuffer_new(); | |||
| if (replay_buffer_ == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "creat restful replay buffer fail"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "create restful replay buffer fail"; | |||
| } | |||
| return status; | |||
| return SUCCESS; | |||
| } | |||
| Status RestfulRequest::RestfulReplay(const std::string &replay) { | |||
| Status status(SUCCESS); | |||
| if (replay_buffer_ == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "replay_buffer_ is nullptr"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "replay_buffer_ is nullptr"; | |||
| } | |||
| if (decompose_event_request_ == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "replay_buffer_ is nullptr"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "replay_buffer_ is nullptr"; | |||
| } | |||
| if (decompose_event_request_->event_request_ == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "replay_buffer_ is nullptr"); | |||
| return status; | |||
| return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "replay_buffer_ is nullptr"; | |||
| } | |||
| evbuffer_add(replay_buffer_, replay.data(), replay.size()); | |||
| evhttp_send_reply(decompose_event_request_->event_request_, HTTP_OK, "Client", replay_buffer_); | |||
| return status; | |||
| return SUCCESS; | |||
| } | |||
| Status RestfulRequest::ErrorMessage(Status status) { | |||
| Status error_status(SUCCESS); | |||
| nlohmann::json error_json = {{"error_message", status.StatusMessage()}}; | |||
| nlohmann::json error_json = {{"error_msg", status.StatusMessage()}}; | |||
| std::string out_error_str = error_json.dump(); | |||
| if ((error_status = RestfulReplay(out_error_str)) != SUCCESS) { | |||
| return error_status; | |||
| @@ -46,16 +46,8 @@ void RestfulServer::DispatchEvHttpRequest(evhttp_request *request) { | |||
| Status status(SUCCESS); | |||
| auto de_request = std::make_unique<DecomposeEvRequest>(request, max_msg_size_); | |||
| if (de_request == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "de_request is nullptr."); | |||
| return; | |||
| } | |||
| Status de_status = de_request->Decompose(); | |||
| auto restful_request = std::make_shared<RestfulRequest>(std::move(de_request)); | |||
| if (restful_request == nullptr) { | |||
| ERROR_INFER_STATUS(status, INVALID_INPUTS, "restful_request is nullptr."); | |||
| return; | |||
| } | |||
| status = restful_request->RestfulReplayBufferInit(); | |||
| if (status != SUCCESS) { | |||
| if ((status = restful_request->ErrorMessage(status)) != SUCCESS) { | |||
| @@ -129,7 +121,7 @@ Status RestfulServer::StartRestfulServer() { | |||
| if (listener == nullptr) { | |||
| status = INFER_STATUS_LOG_ERROR(SYSTEM_ERROR) | |||
| << "Serving Error: RESTful server start failed, create http listener faild, port " << restful_port_; | |||
| << "Serving Error: RESTful server start failed, create http listener failed, port " << restful_port_; | |||
| free_event_base(); | |||
| free_evhttp(); | |||
| return status; | |||
| @@ -137,7 +129,7 @@ Status RestfulServer::StartRestfulServer() { | |||
| auto bound = evhttp_bind_listener(event_http_, listener); | |||
| if (bound == nullptr) { | |||
| status = INFER_STATUS_LOG_ERROR(SYSTEM_ERROR) | |||
| << "Serving Error: RESTful server start failed, bind http listener to server faild, port " | |||
| << "Serving Error: RESTful server start failed, bind http listener to server failed, port " | |||
| << restful_port_; | |||
| evconnlistener_free(listener); | |||
| free_event_base(); | |||
| @@ -27,6 +27,9 @@ | |||
| namespace mindspore::serving { | |||
| PYBIND11_MODULE(_mindspore_serving, m) { | |||
| // avoid as numpy object memory copy in PyTensor::AsPythonData | |||
| py::class_<TensorBase, TensorBasePtr>(m, "Tensor_"); | |||
| py::class_<PyPreprocessStorage, std::shared_ptr<PyPreprocessStorage>>(m, "PreprocessStorage_") | |||
| .def(py::init<>()) | |||
| .def_static("get_instance", &PyPreprocessStorage::Instance) | |||
| @@ -196,7 +196,7 @@ py::object PyTensor::AsPythonData(TensorBasePtr tensor, bool copy) { | |||
| py::buffer_info info(reinterpret_cast<void *>(const_cast<uint8_t *>(data)), sizeof(uint8_t), | |||
| py::format_descriptor<uint8_t>::format(), 1, shape, strides); | |||
| if (!copy) { | |||
| py::array self; | |||
| py::object self = py::cast(tensor.get()); | |||
| return py::array(py::dtype(info), info.shape, info.strides, info.ptr, self); | |||
| } else { | |||
| return py::array(py::dtype(info), info.shape, info.strides, info.ptr); | |||
| @@ -210,7 +210,7 @@ py::object PyTensor::AsPythonData(TensorBasePtr tensor, bool copy) { | |||
| static_cast<ssize_t>(tensor_shape.size()), shape, strides); | |||
| if (!copy) { | |||
| py::array self; | |||
| py::object self = py::cast(tensor.get()); | |||
| return py::array(py::dtype(info), info.shape, info.strides, info.ptr, self); | |||
| } else { | |||
| return py::array(py::dtype(info), info.shape, info.strides, info.ptr); | |||
| @@ -221,7 +221,7 @@ py::object PyTensor::AsPythonData(TensorBasePtr tensor, bool copy) { | |||
| py::tuple PyTensor::AsNumpyTuple(const InstanceData &instance_data) { | |||
| py::tuple numpy_inputs_tuple(instance_data.size()); | |||
| for (size_t i = 0; i < instance_data.size(); i++) { // inputs | |||
| numpy_inputs_tuple[i] = PyTensor::AsPythonData(instance_data[i]); | |||
| numpy_inputs_tuple[i] = PyTensor::AsPythonData(instance_data[i], false); | |||
| } | |||
| return numpy_inputs_tuple; | |||
| } | |||
| @@ -126,17 +126,17 @@ def _check_str(arg_name, str_val): | |||
| raise RuntimeError(f"Parameter '{arg_name}' should not be empty str") | |||
| def _check_int(arg_name, int_val, mininum=None, maximum=None): | |||
| def _check_int(arg_name, int_val, minimum=None, maximum=None): | |||
| """Check whether the input parameters are reasonable int input""" | |||
| if not isinstance(int_val, int): | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be int, but actually {type(int_val)}") | |||
| if mininum is not None and int_val < mininum: | |||
| if minimum is not None and int_val < minimum: | |||
| if maximum is not None: | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be in range [{mininum},{maximum}]") | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be >= {mininum}") | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be in range [{minimum},{maximum}]") | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be >= {minimum}") | |||
| if maximum is not None and int_val > maximum: | |||
| if mininum is not None: | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be in range [{mininum},{maximum}]") | |||
| if minimum is not None: | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be in range [{minimum},{maximum}]") | |||
| raise RuntimeError(f"Parameter '{arg_name}' should be <= {maximum}") | |||
| @@ -259,29 +259,6 @@ class Client: | |||
| print(status_code.value) | |||
| return ClientGrpcAsyncError({"error": "Grpc Error, " + str(status_code.value)}) | |||
| def close(self): | |||
| """ | |||
| Used to release gRPC connection avoiding influence on the connection to the next restarted Serving server. | |||
| Examples: | |||
| >>> from mindspore_serving.client import Client | |||
| >>> import numpy as np | |||
| >>> instance = {"x1": np.ones((2, 2), np.int32), "x2": np.ones((2, 2), np.int32)} | |||
| >>> client = Client("localhost", 5500, "add", "add_cast") | |||
| >>> result = client.infer(instance) | |||
| >>> client.close() | |||
| >>> print(result) | |||
| """ | |||
| if self.stub: | |||
| del self.stub | |||
| self.stub = None | |||
| def __enter__(self): | |||
| return self | |||
| def __exit__(self, exc_type, exc_val, exc_tb): | |||
| self.close() | |||
| def _create_request(self, instances): | |||
| """Used to create request spec.""" | |||
| if not isinstance(instances, (tuple, list)): | |||
| @@ -342,7 +319,7 @@ class Client: | |||
| class ClientGrpcAsyncResult: | |||
| """ | |||
| When Client.infer_async invoke sucessfully, a ClientGrpcAsyncResult object is returned. | |||
| When Client.infer_async invoke successfully, a ClientGrpcAsyncResult object is returned. | |||
| Examples: | |||
| >>> from mindspore_serving.client import Client | |||
| @@ -91,7 +91,7 @@ class _MultiCompatibleRotatingFileHandler(RotatingFileHandler): | |||
| self.stream.close() | |||
| self.stream = None | |||
| # Attain an exclusive lock with bloking mode by `fcntl` module. | |||
| # Attain an exclusive lock with blocking mode by `fcntl` module. | |||
| with open(self.baseFilename, 'a') as file_pointer: | |||
| if platform.system() != "Windows": | |||
| fcntl.lockf(file_pointer.fileno(), fcntl.LOCK_EX) | |||
| @@ -396,7 +396,7 @@ def _clear_handler(logger): | |||
| logger.removeHandler(handler) | |||
| def _find_caller(stack_info=False): | |||
| def _find_caller(stack_info=False, _=1): | |||
| """ | |||
| Find the stack frame of the caller. | |||
| @@ -432,13 +432,13 @@ def _find_caller(stack_info=False): | |||
| def _get_stack_info(frame): | |||
| """ | |||
| Get the stack informations. | |||
| Get the stack information. | |||
| Args: | |||
| frame(frame): the frame requiring informations. | |||
| frame(frame): the frame requiring information. | |||
| Returns: | |||
| str, the string of the stack informations. | |||
| str, the string of the stack information. | |||
| """ | |||
| sinfo = None | |||
| stack_prefix = 'Stack (most recent call last):\n' | |||
| @@ -14,7 +14,6 @@ | |||
| # ============================================================================ | |||
| """MindSpore Serving Master""" | |||
| from mindspore_serving.worker import _check_version | |||
| from ._master import start_grpc_server, start_restful_server, start_master_server, stop | |||
| __all__ = [] | |||
| @@ -1,2 +1,4 @@ | |||
| numpy>=1.17.0 | |||
| protobuf>=3.8.0 | |||
| grpcio>=1.27.3 | |||
| requests>=2.22.0 | |||
| @@ -3,11 +3,7 @@ add_library(protobuf::libprotobuf ALIAS protobuf::protobuf) | |||
| add_executable(protobuf::libprotoc ALIAS protobuf::protoc) | |||
| set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) | |||
| if(CMAKE_CROSSCOMPILING) | |||
| find_program(_PROTOBUF_PROTOC protoc) | |||
| else() | |||
| set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>) | |||
| endif() | |||
| set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>) | |||
| # Find gRPC installation | |||
| # Looks for gRPCConfig.cmake file installed by gRPC's cmake installation. | |||
| @@ -24,13 +20,8 @@ message(STATUS "Using gRPC ${gRPC_VERSION}") | |||
| set(_GRPC_GRPCPP gRPC::grpc++) | |||
| set(_REFLECTION gRPC::grpc++_reflection) | |||
| if(CMAKE_CROSSCOMPILING) | |||
| find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) | |||
| find_program(_GRPC_PYTHON_PLUGIN_EXECUTABLE grpc_python_plugin) | |||
| else() | |||
| set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>) | |||
| set(_GRPC_PYTHON_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_python_plugin>) | |||
| endif() | |||
| set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>) | |||
| set(_GRPC_PYTHON_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_python_plugin>) | |||
| # Proto file | |||
| # Generated sources | |||
| @@ -96,7 +87,7 @@ add_library(serving_ut_common STATIC ${UT_SERVING_COMMON}) | |||
| target_link_libraries(serving_ut_common PRIVATE ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF}) | |||
| target_link_libraries(serving_ut_common PRIVATE mindspore_serving::event mindspore_serving::event_pthreads) | |||
| target_link_libraries(serving_ut_common PRIVATE pthread mindspore::glog) | |||
| target_link_libraries(serving_ut_common PRIVATE pthread mindspore_serving::glog) | |||
| target_link_libraries(serving_ut_common PRIVATE ${SECUREC_LIBRARY}) | |||
| # copy libevent lib | |||
| @@ -14,9 +14,12 @@ | |||
| # limitations under the License. | |||
| # ============================================================================ | |||
| set -e | |||
| BASEPATH=$(cd "$(dirname "$0")"; pwd) | |||
| BASEPATH=$( | |||
| cd "$(dirname "$0")" | |||
| pwd | |||
| ) | |||
| PROJECT_PATH=${BASEPATH}/../../.. | |||
| if [ $BUILD_PATH ];then | |||
| if [ $BUILD_PATH ]; then | |||
| echo "BUILD_PATH = $BUILD_PATH" | |||
| else | |||
| BUILD_PATH=${PROJECT_PATH}/build | |||
| @@ -859,15 +859,26 @@ TEST_F(TestParseReply, test_reply_SUCCESS) { | |||
| std::cout << "===start====" << std::endl; | |||
| for (auto &it : element.items()) { | |||
| if (it.key() == "key_int") { | |||
| ASSERT_EQ(it.value(), 1); | |||
| ASSERT_TRUE(it.value().is_array()); | |||
| ASSERT_EQ(it.value().size(), 1); | |||
| auto array_items = it.value().items(); | |||
| auto int_val = *(array_items.begin()); | |||
| ASSERT_TRUE(int_val.value().is_number_integer()); | |||
| ASSERT_EQ(int_val.value(), 1); | |||
| count++; | |||
| } else if (it.key() == "key_bool") { | |||
| ASSERT_TRUE(it.value().is_boolean()); | |||
| ASSERT_EQ(it.value(), false); | |||
| count++; | |||
| } else if (it.key() == "key_float") { | |||
| ASSERT_FLOAT_EQ(it.value(), 2.3); | |||
| ASSERT_TRUE(it.value().is_array()); | |||
| ASSERT_EQ(it.value().size(), 1); | |||
| auto array_items = it.value().items(); | |||
| auto float_val = *(array_items.begin()); | |||
| ASSERT_FLOAT_EQ(float_val.value(), 2.3); | |||
| count++; | |||
| } else if (it.key() == "key_str") { | |||
| ASSERT_TRUE(it.value().is_string()); | |||
| ASSERT_EQ(it.value(), "ut_test"); | |||
| count++; | |||
| } else if (it.key() == "key_bytes") { | |||
| @@ -44,10 +44,12 @@ echo "LD_LIBRARY_PATH=LD_LIBRARY_PATH" | |||
| unset http_proxy | |||
| unset https_proxy | |||
| cd - | |||
| cd ${PROJECT_PATH}/tests/ut/python/tests/ | |||
| if [ $# -gt 0 ]; then | |||
| pytest -v ${PROJECT_PATH}/tests/ut/python/tests/ -k $1 | |||
| pytest -v . -k "$1" | |||
| else | |||
| pytest -v ${PROJECT_PATH}/tests/ut/python/tests | |||
| pytest -v . | |||
| fi | |||
| RET=$? | |||
| @@ -79,5 +79,43 @@ def serving_test(func): | |||
| finally: | |||
| master.stop() | |||
| worker.stop() | |||
| servable_dir = os.path.join(os.getcwd(), "serving_python_ut_servables") | |||
| rmtree(servable_dir, True) | |||
| return wrap_test | |||
| def release_client(client): | |||
| del client.stub | |||
| client.stub = None | |||
| # test servable_config.py with client | |||
| servable_config_import = r""" | |||
| import numpy as np | |||
| from mindspore_serving.worker import register | |||
| """ | |||
| servable_config_declare_servable = r""" | |||
| register.declare_servable(servable_file="tensor_add.mindir", model_format="MindIR", with_batch_dim=False) | |||
| """ | |||
| servable_config_preprocess_cast = r""" | |||
| def add_trans_datatype(x1, x2): | |||
| return x1.astype(np.float32), x2.astype(np.float32) | |||
| """ | |||
| servable_config_method_add_common = r""" | |||
| @register.register_method(output_names=["y"]) | |||
| def add_common(x1, x2): # only support float32 inputs | |||
| y = register.call_servable(x1, x2) | |||
| return y | |||
| """ | |||
| servable_config_method_add_cast = r""" | |||
| @register.register_method(output_names=["y"]) | |||
| def add_cast(x1, x2): | |||
| x1, x2 = register.call_preprocess(add_trans_datatype, x1, x2) # cast input to float32 | |||
| y = register.call_servable(x1, x2) | |||
| return y | |||
| """ | |||
| @@ -0,0 +1,75 @@ | |||
| # Copyright 2020 Huawei Technologies Co., Ltd | |||
| # | |||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||
| # you may not use this file except in compliance with the License. | |||
| # You may obtain a copy of the License at | |||
| # | |||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, software | |||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| # See the License for the specific language governing permissions and | |||
| # limitations under the License. | |||
| # ============================================================================ | |||
| """test Serving, Common""" | |||
| import json | |||
| import requests | |||
| import numpy as np | |||
| def compare_float_value(expect, result): | |||
| expect = np.array(expect) | |||
| result = np.array(result) | |||
| assert (np.abs(expect - result) < 0.001).all() | |||
| def create_multi_instances_fp32(instance_count): | |||
| instances = [] | |||
| # instance 1 | |||
| y_data_list = [] | |||
| for i in range(instance_count): | |||
| x1 = np.asarray([[1.1, 2.2], [3.3, 4.4]]).astype(np.float32) * (i + 1) | |||
| x2 = np.asarray([[5.5, 6.6], [7.7, 8.8]]).astype(np.float32) * (i + 1) | |||
| y_data_list.append(x1 + x2) | |||
| instances.append({"x1": x1.tolist(), "x2": x2.tolist()}) | |||
| return instances, y_data_list | |||
| def create_multi_instances_int32_input_fp32_output(instance_count): | |||
| instances = [] | |||
| # instance 1 | |||
| y_data_list = [] | |||
| for i in range(instance_count): | |||
| x1 = np.asarray([[1.1, 2.2], [3.3, 4.4]]).astype(np.int32) * (i + 1) | |||
| x2 = np.asarray([[5.5, 6.6], [7.7, 8.8]]).astype(np.int32) * (i + 1) | |||
| y_data_list.append((x1 + x2).astype(np.float32)) | |||
| instances.append({"x1": x1.tolist(), "x2": x2.tolist()}) | |||
| return instances, y_data_list | |||
| def check_result(result, y_data_list, output_name="y"): | |||
| result = result["instances"] | |||
| assert len(result) == len(y_data_list) | |||
| for result_item, expected_item in zip(result, y_data_list): | |||
| result_item = np.array(result_item[output_name]) | |||
| print("result", result_item) | |||
| print("expect:", expected_item) | |||
| assert result_item.shape == expected_item.shape | |||
| assert (np.abs(result_item - expected_item) < 0.001).all() | |||
| def post_restful(ip, restful_port, servable_name, method_name, json_instances, version_number=None): | |||
| instances_map = {"instances": json_instances} | |||
| post_payload = json.dumps(instances_map) | |||
| print("request:", post_payload) | |||
| if version_number is not None: | |||
| request_url = f"http://{ip}:{restful_port}/model/{servable_name}/version/{version_number}:{method_name}" | |||
| result = requests.post(request_url, data=post_payload) | |||
| else: | |||
| request_url = f"http://{ip}:{restful_port}/model/{servable_name}:{method_name}" | |||
| result = requests.post(request_url, data=post_payload) | |||
| print("result", result.text) | |||
| result = json.loads(result.text) | |||
| return result | |||
| @@ -18,7 +18,9 @@ import numpy as np | |||
| from mindspore_serving import master | |||
| from mindspore_serving import worker | |||
| from mindspore_serving.client import Client | |||
| from common import ServingTestBase, serving_test | |||
| from common import ServingTestBase, serving_test, release_client | |||
| from common import servable_config_import, servable_config_declare_servable, servable_config_preprocess_cast | |||
| from common import servable_config_method_add_common, servable_config_method_add_cast | |||
| def create_multi_instances_fp32(instance_count): | |||
| @@ -50,7 +52,7 @@ def test_master_worker_client_success(): | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| client.close() # avoid affecting the next use case | |||
| release_client(client) | |||
| print(result) | |||
| check_result(result, y_data_list) | |||
| @@ -63,11 +65,12 @@ def test_master_worker_client_multi_times_success(): | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) | |||
| master.start_grpc_server("0.0.0.0", 5500) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_common") as client: | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| check_result(result, y_data_list) | |||
| client = Client("localhost", 5500, base.servable_name, "add_common") | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| check_result(result, y_data_list) | |||
| release_client(client) | |||
| @serving_test | |||
| @@ -78,10 +81,11 @@ def test_master_worker_client_alone_success(): | |||
| master.start_grpc_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| with Client("localhost", 5500, base.servable_name, "add_common") as client: | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_common") | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| check_result(result, y_data_list) | |||
| @@ -93,11 +97,12 @@ def test_master_worker_client_alone_multi_times_success(): | |||
| master.start_grpc_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_common") as client: | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| check_result(result, y_data_list) | |||
| client = Client("localhost", 5500, base.servable_name, "add_common") | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = client.infer(instances) | |||
| check_result(result, y_data_list) | |||
| release_client(client) | |||
| @serving_test | |||
| @@ -112,7 +117,7 @@ def test_master_worker_client_async_success(): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result_future = client.infer_async(instances) | |||
| result = result_future.result() | |||
| client.close() # avoid affecting the next use case | |||
| release_client(client) # avoid affecting the next use case | |||
| print(result) | |||
| check_result(result, y_data_list) | |||
| @@ -125,12 +130,13 @@ def test_master_worker_client_async_multi_times_success(): | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) | |||
| master.start_grpc_server("0.0.0.0", 5500) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_common") as client: | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result_future = client.infer_async(instances) | |||
| result = result_future.result() | |||
| check_result(result, y_data_list) | |||
| client = Client("localhost", 5500, base.servable_name, "add_common") | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result_future = client.infer_async(instances) | |||
| result = result_future.result() | |||
| check_result(result, y_data_list) | |||
| release_client(client) | |||
| @serving_test | |||
| @@ -231,7 +237,7 @@ def test_master_worker_client_alone_repeat_grpc_and_restful_port_failed(): | |||
| master.start_restful_server("0.0.0.0", 7600) | |||
| assert False | |||
| except RuntimeError as e: | |||
| assert "Serving Error: RESTful server start failed, create http listener faild, port" in str(e) | |||
| assert "Serving Error: RESTful server start failed, create http listener failed, port" in str(e) | |||
| @serving_test | |||
| @@ -246,37 +252,6 @@ def test_master_worker_client_alone_repeat_grpc_and_restful_port2_failed(): | |||
| assert "Serving Error: Serving gRPC server start failed, create server failed, address" in str(e) | |||
| # test servable_config.py with client | |||
| servable_config_import = r""" | |||
| import numpy as np | |||
| from mindspore_serving.worker import register | |||
| """ | |||
| servable_config_declare_servable = r""" | |||
| register.declare_servable(servable_file="tensor_add.mindir", model_format="MindIR", with_batch_dim=False) | |||
| """ | |||
| servable_config_preprocess_cast = r""" | |||
| def add_trans_datatype(x1, x2): | |||
| return x1.astype(np.float32), x2.astype(np.float32) | |||
| """ | |||
| servable_config_method_add_common = r""" | |||
| @register.register_method(output_names=["y"]) | |||
| def add_common(x1, x2): # only support float32 inputs | |||
| y = register.call_servable(x1, x2) | |||
| return y | |||
| """ | |||
| servable_config_method_add_cast = r""" | |||
| @register.register_method(output_names=["y"]) | |||
| def add_cast(x1, x2): | |||
| x1, x2 = register.call_preprocess(add_trans_datatype, x1, x2) # cast input to float32 | |||
| y = register.call_servable(x1, x2) | |||
| return y | |||
| """ | |||
| @serving_test | |||
| def test_master_worker_client_servable_content_success(): | |||
| base = ServingTestBase() | |||
| @@ -292,8 +267,9 @@ def test_master_worker_client_servable_content_success(): | |||
| # Client | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| with Client("localhost", 5500, base.servable_name, "add_common") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_common") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| print(result) | |||
| check_result(result, y_data_list) | |||
| @@ -320,9 +296,9 @@ def add_cast(x1, x2): | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| print(result) | |||
| assert "Preprocess Failed" in str(result[0]["error"]) | |||
| @@ -349,9 +325,9 @@ def add_cast(x1, x2): | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| print(result) | |||
| assert "Postprocess Failed" in str(result[0]["error"]) | |||
| @@ -387,9 +363,9 @@ def add_cast(x1, x2, label): | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = list_str[i] | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| assert result[0]["text"] == "ABC123" | |||
| assert result[1]["text"] == "DEF456" | |||
| assert result[2]["text"] == "HIJ789" | |||
| @@ -426,9 +402,9 @@ def add_cast(x1, x2, label): | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = str.encode(list_str[i]) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| assert bytes.decode(result[0]["text"]) == "ABC123" | |||
| assert bytes.decode(result[1]["text"]) == "DEF456" | |||
| assert bytes.decode(result[2]["text"]) == "HIJ789" | |||
| @@ -458,9 +434,9 @@ def add_cast(x1, x2, bool_val): | |||
| for i, instance in enumerate(instances): | |||
| instance["bool_val"] = (i % 2 == 0) | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| assert not result[0]["value"] | |||
| assert result[1]["value"] | |||
| assert not result[2]["value"] | |||
| @@ -490,9 +466,9 @@ def add_cast(x1, x2, int_val): | |||
| for i, instance in enumerate(instances): | |||
| instance["int_val"] = i * 2 | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| assert result[0]["value"] == 1 | |||
| assert result[1]["value"] == 3 | |||
| assert result[2]["value"] == 5 | |||
| @@ -522,9 +498,53 @@ def add_cast(x1, x2, float_val): | |||
| for i, instance in enumerate(instances): | |||
| instance["float_val"] = i * 2.2 | |||
| # Client, use with avoid affecting the next use case | |||
| with Client("localhost", 5500, base.servable_name, "add_cast") as client: | |||
| result = client.infer(instances) | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| assert result[0]["value"] == 1 | |||
| assert result[1]["value"] == (2.2 + 1) | |||
| assert result[2]["value"] == (4.4 + 1) | |||
| @serving_test | |||
| def test_master_worker_client_preprocess_update_numpy_success(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| def preprocess(x3): | |||
| x3[0] = 123 | |||
| return x3 | |||
| def postprocess(x3, x4): | |||
| return x3 + 1, x4 + 2 | |||
| @register.register_method(output_names=["x3", "x4"]) | |||
| def add_cast(x1, x2, x3): | |||
| x4 = register.call_preprocess(preprocess, x3) # [123, 1, 1], expect x3 is x4, same as python function call | |||
| y = register.call_servable(x1, x2) | |||
| x3, x4 = register.call_postprocess(postprocess, x3, x4) | |||
| return x3, x4 | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_grpc_server("0.0.0.0", 5500) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for instance in instances: | |||
| instance["x3"] = np.ones([3]) | |||
| # Client, use with avoid affecting the next use case | |||
| client = Client("localhost", 5500, base.servable_name, "add_cast") | |||
| result = client.infer(instances) | |||
| release_client(client) | |||
| x3 = np.array([123, 1, 1]) + 1 | |||
| x4 = np.array([123, 1, 1]) + 2 | |||
| assert (result[0]["x3"] == x3).all() | |||
| assert (result[1]["x3"] == x3).all() | |||
| assert (result[2]["x3"] == x3).all() | |||
| assert (result[0]["x4"] == x4).all() | |||
| assert (result[1]["x4"] == x4).all() | |||
| assert (result[2]["x4"] == x4).all() | |||
| @@ -0,0 +1,949 @@ | |||
| # Copyright 2020 Huawei Technologies Co., Ltd | |||
| # | |||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||
| # you may not use this file except in compliance with the License. | |||
| # You may obtain a copy of the License at | |||
| # | |||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, software | |||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| # See the License for the specific language governing permissions and | |||
| # limitations under the License. | |||
| # ============================================================================ | |||
| """test Serving RESTful, with master, worker and client""" | |||
| import base64 | |||
| import numpy as np | |||
| from common import ServingTestBase, serving_test | |||
| from common import servable_config_import, servable_config_declare_servable | |||
| from common_restful import create_multi_instances_fp32, check_result, post_restful | |||
| from mindspore_serving import master | |||
| from mindspore_serving import worker | |||
| def start_str_restful_server(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| index = 0 | |||
| list_str = ["123", "456", "789"] | |||
| def postprocess(y, label): | |||
| global index | |||
| text = list_str[index] | |||
| index = (index + 1) if index + 1 < len(list_str) else 0 | |||
| return y.astype(np.int32), label + text | |||
| @register.register_method(output_names=["y", "text"]) | |||
| def add_common(x1, x2, label): | |||
| y = register.call_servable(x1, x2) | |||
| y, text = register.call_postprocess(postprocess, y, label) | |||
| return y, text | |||
| def empty_postprocess(y, label): | |||
| global index | |||
| if len(label) == 0: | |||
| text = list_str[index] | |||
| else: | |||
| text = "" | |||
| index = (index + 1) if index + 1 < len(list_str) else 0 | |||
| return y.astype(np.int32), text | |||
| @register.register_method(output_names=["y", "text"]) | |||
| def add_empty(x1, x2, label): | |||
| y = register.call_servable(x1, x2) | |||
| y, text = register.call_postprocess(empty_postprocess, y, label) | |||
| return y, text | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| return base | |||
| def start_bytes_restful_server(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| index = 0 | |||
| list_str = ["123", "456", "789"] | |||
| def postprocess(y, label): | |||
| global index | |||
| label = bytes.decode(label.tobytes()) # bytes decode to str | |||
| text = list_str[index] | |||
| index = (index + 1) if index + 1 < len(list_str) else 0 | |||
| return y.astype(np.int32), str.encode(label + text) # str encode to bytes | |||
| @register.register_method(output_names=["y", "text"]) | |||
| def add_common(x1, x2, label): | |||
| y = register.call_servable(x1, x2) | |||
| y, text = register.call_postprocess(postprocess, y, label) | |||
| return y, text | |||
| def empty_postprocess(y, label): | |||
| global index | |||
| label = bytes.decode(label.tobytes()) # bytes decode to str | |||
| if len(label) == 0: | |||
| text = list_str[index] | |||
| else: | |||
| text = "" | |||
| index = (index + 1) if index + 1 < len(list_str) else 0 | |||
| return y.astype(np.int32), str.encode(text) # str encode to bytes | |||
| @register.register_method(output_names=["y", "text"]) | |||
| def add_empty(x1, x2, label): | |||
| y = register.call_servable(x1, x2) | |||
| y, text = register.call_postprocess(empty_postprocess, y, label) | |||
| return y, text | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| return base | |||
| def start_bool_int_float_restful_server(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| def bool_postprocess(y, bool_val): | |||
| return y.astype(np.int32), ~bool_val | |||
| @register.register_method(output_names=["y", "value"]) | |||
| def add_bool(x1, x2, bool_val): | |||
| y = register.call_servable(x1, x2) | |||
| y, value = register.call_postprocess(bool_postprocess, y, bool_val) | |||
| return y, value | |||
| def int_postprocess(y, int_val): | |||
| return y.astype(np.int32), int_val + 1 | |||
| @register.register_method(output_names=["y", "value"]) | |||
| def add_int(x1, x2, int_val): | |||
| y = register.call_servable(x1, x2) | |||
| y, value = register.call_postprocess(int_postprocess, y, int_val) | |||
| return y, value | |||
| def float_postprocess(y, float_val): | |||
| value = float_val + 1 | |||
| if value.dtype == np.float16: | |||
| value = value.astype(np.float32) | |||
| return y, value | |||
| @register.register_method(output_names=["y", "value"]) | |||
| def add_float(x1, x2, float_val): | |||
| y = register.call_servable(x1, x2) | |||
| y, value = register.call_postprocess(float_postprocess, y, float_val) | |||
| return y, value | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| return base | |||
| def common_test_restful_base64_str_scalar_input_output_success(shape): | |||
| base = start_str_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| if shape is None: | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"} | |||
| else: | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str", | |||
| 'shape': shape} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| result = result["instances"] | |||
| assert result[0]["text"] == "ABC123" | |||
| assert result[1]["text"] == "DEF456" | |||
| assert result[2]["text"] == "HIJ789" | |||
| @serving_test | |||
| def test_restful_base64_str_scalar_input_output_success(): | |||
| common_test_restful_base64_str_scalar_input_output_success(shape=None) | |||
| @serving_test | |||
| def test_restful_base64_str_scalar_shape1_input_output_success(): | |||
| common_test_restful_base64_str_scalar_input_output_success(shape=[1]) | |||
| @serving_test | |||
| def test_restful_base64_str_scalar_shape_empty_input_output_success(): | |||
| common_test_restful_base64_str_scalar_input_output_success(shape=[]) | |||
| @serving_test | |||
| def test_restful_base64_empty_str_input_output_success(): | |||
| base = start_str_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_empty", instances) | |||
| result = result["instances"] | |||
| assert result[0]["text"] == "" | |||
| assert result[1]["text"] == "456" | |||
| assert result[2]["text"] == "" | |||
| @serving_test | |||
| def test_restful_base64_str_scalar_invalid_shape0_input_failed(): | |||
| base = start_str_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str", "shape": [0]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| assert "only support scalar when data type is string or bytes, please check 'type' or 'shape'" \ | |||
| in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_base64_str_scalar_invalid_shape_input_failed(): | |||
| base = start_str_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str", 'shape': [2]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| assert "json object, only support scalar when data type is string or bytes, please check 'type' or 'shape'" \ | |||
| in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_base64_str_1d_array_failed(): | |||
| base = start_str_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = [{"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"}, | |||
| {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"}] | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| assert "json array, string or bytes type only support one item" in str(result["error_msg"]) | |||
| def common_test_restful_bytes_input_output_success(shape): | |||
| base = start_bytes_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| if shape is not None: | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "shape": shape} | |||
| else: | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode()} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| result = result["instances"] | |||
| b64_decode_to_str = lambda a: bytes.decode(base64.b64decode(a["b64"])) | |||
| assert b64_decode_to_str(result[0]["text"]) == "ABC123" | |||
| assert b64_decode_to_str(result[1]["text"]) == "DEF456" | |||
| assert b64_decode_to_str(result[2]["text"]) == "HIJ789" | |||
| @serving_test | |||
| def test_restful_bytes_input_output_success(): | |||
| common_test_restful_bytes_input_output_success(None) | |||
| @serving_test | |||
| def test_restful_bytes_empty_shape_success(): | |||
| common_test_restful_bytes_input_output_success([]) | |||
| @serving_test | |||
| def test_restful_bytes_shape1_success(): | |||
| common_test_restful_bytes_input_output_success([1]) | |||
| @serving_test | |||
| def test_restful_empty_bytes_input_output_success(): | |||
| base = start_bytes_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode()} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_empty", instances) | |||
| result = result["instances"] | |||
| b64_decode_to_str = lambda a: bytes.decode(base64.b64decode(a["b64"])) | |||
| assert b64_decode_to_str(result[0]["text"]) == "" | |||
| assert b64_decode_to_str(result[1]["text"]) == "456" | |||
| assert b64_decode_to_str(result[2]["text"]) == "" | |||
| @serving_test | |||
| def test_restful_bytes_1d_array_failed(): | |||
| base = start_bytes_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = [{"b64": base64.b64encode(str.encode(list_str[i])).decode()}, | |||
| {"b64": base64.b64encode(str.encode(list_str[i])).decode()}] | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| assert "json array, string or bytes type only support one item" in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_bytes_invalid_shape_input_failed(): | |||
| base = start_bytes_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| list_str = ["ABC", "DEF", "HIJ"] | |||
| for i, instance in enumerate(instances): | |||
| instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), 'shape': [0]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| assert "only support scalar when data type is string or bytes, please check 'type' or 'shape'" \ | |||
| in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_bool_scalar_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| val = np.int8(i % 2 == 0) | |||
| instance["bool_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "bool"} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_bool", instances) | |||
| result = result["instances"] | |||
| assert not result[0]["value"] | |||
| assert result[1]["value"] | |||
| assert not result[2]["value"] | |||
| @serving_test | |||
| def test_restful_base64_bool_1d_array_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| val = [(i % 2 == 0)] * (i + 1) | |||
| val = np.array(val) | |||
| instance["bool_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "bool", "shape": [i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_bool", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [False] | |||
| assert result[1]["value"] == [True, True] | |||
| assert result[2]["value"] == [False, False, False] | |||
| @serving_test | |||
| def test_restful_base64_bool_2d_array_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| val = (i % 2 == 0) | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val) | |||
| instance["bool_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "bool", | |||
| "shape": [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_bool", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [[False]] | |||
| assert result[1]["value"] == [[True, True], [True, True]] | |||
| assert result[2]["value"] == [[False, False, False], [False, False, False], [False, False, False]] | |||
| @serving_test | |||
| def test_restful_base64_int_scalar_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| val = np.int32(i * 2) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32"} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == 1 | |||
| assert result[1]["value"] == 3 | |||
| assert result[2]["value"] == 5 | |||
| @serving_test | |||
| def test_restful_base64_int_1d_empty_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| if i % 2 == 0: | |||
| val = [] | |||
| else: | |||
| val = [i * 2] * (i + 1) | |||
| val = np.array(val).astype(np.int32) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": val.shape} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [] | |||
| assert result[1]["value"] == [3, 3] | |||
| assert result[2]["value"] == [] | |||
| @serving_test | |||
| def test_restful_base64_int_2d_empty_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| if i % 2 == 0: | |||
| val = [[]] | |||
| else: | |||
| val = [i * 2] * (i + 1) | |||
| val = np.array(val).astype(np.int32) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": val.shape} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [[]] | |||
| assert result[1]["value"] == [3, 3] | |||
| assert result[2]["value"] == [[]] | |||
| @serving_test | |||
| def test_restful_base64_int_2d_empty_invalid_shape_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for _, instance in enumerate(instances): | |||
| val = [[]] | |||
| val = np.array(val).astype(np.int32) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": [1, 2, 0, 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| assert "json object, key is 'shape', invalid shape value" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_int_1d_array_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2 | |||
| val = [val] * (i + 1) | |||
| val = np.array(val).astype(np.int32) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": val.shape} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [1] | |||
| assert result[1]["value"] == [3, 3] | |||
| assert result[2]["value"] == [5, 5, 5] | |||
| def common_test_restful_base64_int_type_2d_array_input_output_success(dtype): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype_str_map = {np.int8: "int8", np.int16: "int16", np.int32: "int32", np.int64: "int64"} | |||
| assert dtype in dtype_str_map | |||
| for i, instance in enumerate(instances): | |||
| val = (i + 1) * 2 * (-1 if i % 2 == 0 else 1) # -2, 4, -6 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| "shape": val.shape} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [[-1]] | |||
| assert result[1]["value"] == [[5, 5], [5, 5]] | |||
| assert result[2]["value"] == [[-5, -5, -5], [-5, -5, -5], [-5, -5, -5]] | |||
| @serving_test | |||
| def test_restful_base64_int8_2d_array_input_output_success(): | |||
| common_test_restful_base64_int_type_2d_array_input_output_success(np.int8) | |||
| @serving_test | |||
| def test_restful_base64_int16_2d_array_input_output_success(): | |||
| common_test_restful_base64_int_type_2d_array_input_output_success(np.int16) | |||
| @serving_test | |||
| def test_restful_base64_int32_2d_array_input_output_success(): | |||
| common_test_restful_base64_int_type_2d_array_input_output_success(np.int32) | |||
| @serving_test | |||
| def test_restful_base64_int64_2d_array_input_output_success(): | |||
| common_test_restful_base64_int_type_2d_array_input_output_success(np.int64) | |||
| def common_test_restful_base64_uint_type_2d_array_input_output_success(dtype): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype_str_map = {np.uint8: "uint8", np.uint16: "uint16", np.uint32: "uint32", np.uint64: "uint64"} | |||
| assert dtype in dtype_str_map | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| "shape": val.shape} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == [[1]] | |||
| assert result[1]["value"] == [[3, 3], [3, 3]] | |||
| assert result[2]["value"] == [[5, 5, 5], [5, 5, 5], [5, 5, 5]] | |||
| @serving_test | |||
| def test_restful_base64_uint8_2d_array_input_output_success(): | |||
| common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint8) | |||
| @serving_test | |||
| def test_restful_base64_uint16_2d_array_input_output_success(): | |||
| common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint16) | |||
| @serving_test | |||
| def test_restful_base64_uint32_2d_array_input_output_success(): | |||
| common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint32) | |||
| @serving_test | |||
| def test_restful_base64_uint64_2d_array_input_output_success(): | |||
| common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint64) | |||
| @serving_test | |||
| def test_restful_base64_float_scalar_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| for i, instance in enumerate(instances): | |||
| val = np.float32(i * 2.2) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp32"} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| result = result["instances"] | |||
| assert result[0]["value"] == 1.0 | |||
| assert abs(result[1]["value"] - (2.2 + 1)) < 0.001 | |||
| assert abs(result[2]["value"] - (4.4 + 1)) < 0.001 | |||
| @serving_test | |||
| def test_restful_base64_float_1d_array_input_output_success(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = [i * 2.2 * (-1 if i % 2 == 0 else 1)] * (i + 1) # [0], [2.2, 2.2], [-4.4, -4.4, -4.4] | |||
| val = np.array(val).astype(np.float32) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp32", 'shape': [i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| check_result(result, y_data_list, "value") | |||
| def common_test_restful_base64_float_type_2d_array_input_output_success(dtype): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| 'shape': [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| check_result(result, y_data_list, "value") | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_input_output_success(): | |||
| common_test_restful_base64_float_type_2d_array_input_output_success(np.float16) | |||
| @serving_test | |||
| def test_restful_base64_float32_2d_array_input_output_success(): | |||
| common_test_restful_base64_float_type_2d_array_input_output_success(np.float32) | |||
| @serving_test | |||
| def test_restful_base64_float64_2d_array_input_output_success(): | |||
| common_test_restful_base64_float_type_2d_array_input_output_success(np.float64) | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_not_support_fp16_output_failed(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| def postprocess(y, float_val): | |||
| return y, float_val + 1 | |||
| @register.register_method(output_names=["y", "value"]) | |||
| def add_cast(x1, x2, float_val): | |||
| y = register.call_servable(x1, x2) | |||
| y, value = register.call_postprocess(postprocess, y, float_val) | |||
| return y, value | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| 'shape': [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| assert "fp16 reply is not supported" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_dtype_unknow_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "dtype_unknow", | |||
| 'shape': [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, json object, specified type:'dtype_unknow' is illegal" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_dtype_empty_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "", | |||
| 'shape': [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, json object, specified type:'' is illegal" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_shape_not_match1_large_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 1)] * (i + 1) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| 'shape': [i + 2, i + 2]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_shape_not_match2_small_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| 'shape': [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_shape_not_match3_small_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} | |||
| assert dtype in dtype_str_map | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], | |||
| 'shape': [i + 2, i]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_dtype_not_match4_empty_data_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = [[]] | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", | |||
| 'shape': [i + 1, i + 1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_dtype_not_match5_empty_shape_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", | |||
| 'shape': []} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_dtype_not_match6_empty_shape3_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16"} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_dtype_not_match_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp32", | |||
| 'shape': [i + 2, i + 2]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "Parser request failed, size is not matched" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_invalid_shape_2d_shape_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": [[]]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_invalid_shape2_str_shape_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": ["abc"]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_invalid_shape3_float_shape_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": [1.1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] | |||
| @serving_test | |||
| def test_restful_base64_float16_2d_array_invalid_shape4_negative_shape_failed(): | |||
| base = start_bool_int_float_restful_server() | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| dtype = np.float16 | |||
| y_data_list = [] | |||
| for i, instance in enumerate(instances): | |||
| val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 | |||
| val = [[val] * (i + 2)] * (i + 2) | |||
| val = np.array(val).astype(dtype) | |||
| y_data_list.append(val + 1) | |||
| instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": [-1]} | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) | |||
| assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] | |||
| @@ -0,0 +1,335 @@ | |||
| # Copyright 2020 Huawei Technologies Co., Ltd | |||
| # | |||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||
| # you may not use this file except in compliance with the License. | |||
| # You may obtain a copy of the License at | |||
| # | |||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||
| # | |||
| # Unless required by applicable law or agreed to in writing, software | |||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| # See the License for the specific language governing permissions and | |||
| # limitations under the License. | |||
| # ============================================================================ | |||
| """test Serving RESTful, with master, worker and client""" | |||
| import json | |||
| import requests | |||
| from mindspore_serving import master | |||
| from mindspore_serving import worker | |||
| from common import ServingTestBase, serving_test | |||
| from common import servable_config_import, servable_config_declare_servable | |||
| from common_restful import create_multi_instances_fp32, check_result, post_restful | |||
| from common_restful import create_multi_instances_int32_input_fp32_output | |||
| @serving_test | |||
| def test_restful_request_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_multi_times_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_multi_times_int32_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_int32_input_fp32_output(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_multi_times_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| for instance_count in range(1, 5): | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_servable_invalid_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name + "_error", "add_common", instances) | |||
| assert "servable is not available" in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_method_invalid_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common" + "_error", instances) | |||
| assert "method is not available" in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_with_version_number_0_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, 0) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_with_version_number_1_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, 1) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_with_version_number_2_invalid_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, 2) | |||
| assert "servable is not available" in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_version_number_negative_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, -1) | |||
| assert "please check url, version number range failed" in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_without_model_invalid_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| instances_map = {"instances": instances} | |||
| post_payload = json.dumps(instances_map) | |||
| print("request:", post_payload) | |||
| request_url = f"http://localhost:5500/x/:add_common" | |||
| result = requests.post(request_url, data=post_payload) | |||
| print("result", result.text) | |||
| result = json.loads(result.text) | |||
| assert "please check url, the keyword:[model] must contain" in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_without_method_invalid_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| instances_map = {"instances": instances} | |||
| post_payload = json.dumps(instances_map) | |||
| print("request:", post_payload) | |||
| request_url = f"http://localhost:5500/model/{base.servable_name}" | |||
| result = requests.post(request_url, data=post_payload) | |||
| print("result", result.text) | |||
| result = json.loads(result.text) | |||
| assert "please check url, the keyword:[service method] must contain." in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_without_method_invalid_failed(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| instances_map = {"instances": instances} | |||
| post_payload = json.dumps(instances_map) | |||
| print("request:", post_payload) | |||
| request_url = f"http://localhost:5500/model/{base.servable_name}" | |||
| result = requests.post(request_url, data=post_payload) | |||
| print("result", result.text) | |||
| result = json.loads(result.text) | |||
| assert "please check url, the keyword:[service method] must contain." in str(result["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_servable_version_reverse_success(): | |||
| base = ServingTestBase() | |||
| base.init_servable(1, "add_servable_config.py") | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, y_data_list = create_multi_instances_fp32(instance_count) | |||
| instances_map = {"instances": instances} | |||
| post_payload = json.dumps(instances_map) | |||
| print("request:", post_payload) | |||
| request_url = f"http://localhost:5500/version/0/model/{base.servable_name}:add_common" | |||
| result = requests.post(request_url, data=post_payload) | |||
| print("result", result.text) | |||
| result = json.loads(result.text) | |||
| check_result(result, y_data_list) | |||
| @serving_test | |||
| def test_restful_request_preprocess_outputs_count_not_match_failed(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| def add_trans_datatype(x1, x2): | |||
| return x1.astype(np.float32) | |||
| @register.register_method(output_names=["y"]) | |||
| def add_cast(x1, x2): | |||
| x1, x2 = register.call_preprocess(add_trans_datatype, x1, x2) # cast input to float32 | |||
| y = register.call_servable(x1, x2) | |||
| return y | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| print(result) | |||
| assert "Preprocess Failed" in str(result["instances"][0]["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_postprocess_outputs_count_not_match_failed(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| def add_trans_datatype(x1, x2): | |||
| return x1.astype(np.float32) | |||
| @register.register_method(output_names=["y"]) | |||
| def add_cast(x1, x2): | |||
| y = register.call_servable(x1, x2) | |||
| y, y2 = register.call_postprocess(add_trans_datatype, y, x2) | |||
| return y | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| worker.start_servable_in_master(base.servable_dir, base.servable_name) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) | |||
| assert "Postprocess Failed" in str(result["instances"][0]["error_msg"]) | |||
| @serving_test | |||
| def test_restful_request_worker_alone_outputs_count_not_match_failed(): | |||
| base = ServingTestBase() | |||
| servable_content = servable_config_import | |||
| servable_content += servable_config_declare_servable | |||
| servable_content += r""" | |||
| def add_trans_datatype(x1, x2): | |||
| return x1.astype(np.float32) | |||
| @register.register_method(output_names=["y"]) | |||
| def add_cast(x1, x2): | |||
| y = register.call_servable(x1, x2) | |||
| y, y2 = register.call_postprocess(add_trans_datatype, y, x2) | |||
| return y | |||
| """ | |||
| base.init_servable_with_servable_config(1, servable_content) | |||
| master.start_master_server(master_port=7600) | |||
| master.start_restful_server("0.0.0.0", 5500) | |||
| worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) | |||
| # Client | |||
| instance_count = 3 | |||
| instances, _ = create_multi_instances_fp32(instance_count) | |||
| result = post_restful("localhost", 5500, base.servable_name + "_error", "add_common", instances) | |||
| assert "servable is not available" in str(result["error_msg"]) | |||