diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.json" index b4f7902b351024ae2531bb964515947c0d06dba1..c1a5010d15ead5e56514f065912bad18d968e0e0 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.json" @@ -1,17 +1,8 @@ { - "one_line": { - "ErrorCode.internal_ret_2_http(error_code)": [ - "str(error_code)" - ], - "ErrorCode(Enum)": [ - "ErrorCode(object)" - ], - "ret['err'].name.lower()": [ - "ret['err'].name" - ] - }, - "source": "error_code.py", + "source": "error_code.md", "depends": [], "exercise_id": 153, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.md" new file mode 100644 index 0000000000000000000000000000000000000000..72abdc5d8d50da4f190a7ec918e65a9be5d2a481 --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/error_code.md" @@ -0,0 +1,91 @@ +# 错误码处理 + +编写一个错误码枚举,支持转换成字符串格式方法: internal_ret_2_http + +```python +# -*- coding: UTF-8 -*- +from enum import Enum + +class ErrorCode(Enum): + # TODO(You): 请在此定义错误码 + + @staticmethod + def internal_ret_2_http(ret): + ret['err'] = ret['err'].name.lower() + +if __name__ == '__main__': + ret = {'err': ErrorCode.NOT_FOUND} + ErrorCode.internal_ret_2_http(ret) + assert ret['err'] == 'not_found' +``` + +以下对错误码定义 **正确** 的是?。 + +## template + +```python +from enum import Enum + + +class ErrorCode(Enum): + SUCCESS = 0 + FAILED = 1 + NOT_FOUND = 2 + ALREADY_EXIST = 3 + INVALID_PARAMETERS = 4 + + @staticmethod + def internal_ret_2_http(ret): + ret['err'] = ret['err'].name.lower() + +if __name__ == '__main__': + ret = {'err': ErrorCode.NOT_FOUND} + ErrorCode.internal_ret_2_http(ret) + assert ret['err'] == 'not_found' +``` + +## 答案 + +```python +class ErrorCode(Enum): + SUCCESS = 0 + FAILED = 1 + NOT_FOUND = 2 + ALREADY_EXIST = 3 + INVALID_PARAMETERS = 4 +``` + +## 选项 + +### A + +```python +class ErrorCode(Enum): + SUCCESS: 0 + FAILED: 1 + NOT_FOUND: 2 + ALREADY_EXIST: 3 + INVALID_PARAMETERS: 4 +``` + +### B + +```python +class ErrorCode(Enum): + SUCCESS + FAILED + NOT_FOUND + ALREADY_EXIST + INVALID_PARAMETERS +``` + +### C + +```python +class ErrorCode(Enum): + 'SUCCESS' = 0 + 'FAILED' = 1 + 'NOT_FOUND' = 2 + 'ALREADY_EXIST' = 3 + 'INVALID_PARAMETERS' = 4 +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.json" index f21403eda95baa96824060e50ac928840eed19ea..402047bef8f3b24e10d5475a86255985f8175f9b 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.json" @@ -1,22 +1,10 @@ { - "one_line": { - "req_path is None": [ - "req_path is not None" - ], - "self.routes.get(req_path) is None": [ - "self.routes.get(req_path) is not None" - ], - "handler(req), 200": [ - "self.routes.get(req_path)(req), 503" - ], - "lambda req:": [ - "lambda path, data:" - ] - }, - "source": "router.py", + "source": "router.md", "depends": [ "error_code.py" ], "exercise_id": 154, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.md" new file mode 100644 index 0000000000000000000000000000000000000000..11f3d0851051a71a86b8c628ec52ea8d2d392216 --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/router.md" @@ -0,0 +1,225 @@ +# 路由器 + +下面是一个 HTTP 状态代码的定义: + +* 2xx:成功 + * 200 正常,请求已完成。 + * 201 正常,紧接POST命令。 + * 202 正常,已接受用于处理,但处理尚未完成。 + * 203 正常,部分信息—返回的信息只是一部分。 + * 204 正常,无响应—已接收请求,但不存在要回送的信息。 +* 3xx:重定向 + * 301 已移动,请求的数据具有新的位置且更改是永久的。 + * 302 已找到,请求的数据临时具有不同 URI。 + * 303 请参阅其它,可在另一 URI 下找到对请求的响应,且应使用 GET 方法检索此响应。 + * 304 未修改,未按预期修改文档。 + * 305 使用代理,必须通过位置字段中提供的代理来访问请求的资源。 + * 306 未使用,不再使用,保留此代码以便将来使用。 +* 4xx:客户机中出现的错误 + * 400 错误请求,请求中有语法问题,或不能满足请求。 + * 401 未授权,未授权客户机访问数据。 + * 402 需要付款,表示计费系统已有效。 + * 403 禁止,即使有授权也不需要访问。 + * 404 找不到,服务器找不到给定的资源;文档不存在。 + * 407 代理认证请求,客户机首先必须使用代理认证自身。 + * 415 介质类型不受支持,服务器拒绝服务请求,因为不支持请求实体的格式。 +* 5xx:服务器中出现的错误 + * 500 内部错误,因为意外情况,服务器不能完成请求。 + * 501 未执行,服务器不支持请求的工具。 + * 502 错误网关,服务器接收到来自上游服务器的无效响应。 + * 503 无法获得服务,由于临时过载或维护,服务器无法处理请求。 + +编写一个路由服务,支持注入路由配置,正确处理请求参数 + +```python +# -*- coding: UTF-8 -*- +from error_code import ErrorCode +import json +import logging +import traceback +logger = logging.getLogger(__name__) + +class Router: + def __init__(self, routes) -> None: + self.routes = routes + + def dispatch(self, http_request_str): + # TODO(You): 请实现路由逻辑,返回符合语义的HTTP状态码 + +if __name__ == '__main__': + # 注册路由 + router = Router({ + '/': lambda req: print(f'goto home:{str(req)}') + }) + + # 分发路由 + router.dispatch(json.dumps({ + 'path': '/', + 'data': ["Hello", "World!"] + })) +``` + +以下路由的逻辑代码都一样,请选出**HTTP状态码符合语义**的选项。 + +## template + +```python +from error_code import ErrorCode +import json +import logging +import traceback +logger = logging.getLogger(__name__) + + +class Router: + def __init__(self, routes) -> None: + self.routes = routes + + def dispatch(self, http_request_str): + ''' 实现一个路由器 + http_request_str: 请求数据字符串,包含 {path:"",data:""}格式的JSON字符串 + ''' + assert http_request_str is not None + assert type(http_request_str) == type('') + + req_path = None + req = None + try: + http_request = json.loads(http_request_str) + req_path = http_request.get('path') + req = http_request.get('data') + except Exception as e: + logger.error(f"parse data exception:{str(e)}") + return {'err': ErrorCode.INVALID_PARAMETERS}, 200 + + if req_path is None or self.routes.get(req_path) is None: + return {'err': ErrorCode.NOT_FOUND}, 404 + + try: + handler = self.routes.get(req_path) + return handler(req), 200 + except Exception as e: + logger.error(f"route to '{req_path}' exception:{str(e)}") + logger.error(traceback.format_exc()) + return {'err': ErrorCode.FAILED}, 500 + + +if __name__ == '__main__': + server = Router({ + '/': lambda req: print(f'goto home:{str(req)}') + }) + server.dispatch(json.dumps({ + 'path': '/', + 'data': ["Hello", "World!"] + })) +``` + +## 答案 + +```python +class Router: + def dispatch(self, http_request_str): + req_path = None + req = None + try: + http_request = json.loads(http_request_str) + req_path = http_request.get('path') + req = http_request.get('data') + except Exception as e: + logger.error(f"parse data exception:{str(e)}") + return {'err': ErrorCode.INVALID_PARAMETERS}, 400 + + if req_path is None or self.routes.get(req_path) is None: + return {'err': ErrorCode.NOT_FOUND}, 404 + + try: + handler = self.routes.get(req_path) + return handler(req), 200 + except Exception as e: + logger.error(f"route to '{req_path}' exception:{str(e)}") + logger.error(traceback.format_exc()) + return {'err': ErrorCode.FAILED}, 500 +``` + +## 选项 + +### A + +```python +class Router: + def dispatch(self, http_request_str): + req_path = None + req = None + try: + http_request = json.loads(http_request_str) + req_path = http_request.get('path') + req = http_request.get('data') + except Exception as e: + logger.error(f"parse data exception:{str(e)}") + return {'err': ErrorCode.INVALID_PARAMETERS}, 200 + + if req_path is None or self.routes.get(req_path) is None: + return {'err': ErrorCode.NOT_FOUND}, 200 + + try: + handler = self.routes.get(req_path) + return handler(req), 200 + except Exception as e: + logger.error(f"route to '{req_path}' exception:{str(e)}") + logger.error(traceback.format_exc()) + return {'err': ErrorCode.FAILED}, 200 +``` + +### B + +```python +class Router: + def dispatch(self, http_request_str): + req_path = None + req = None + try: + http_request = json.loads(http_request_str) + req_path = http_request.get('path') + req = http_request.get('data') + except Exception as e: + logger.error(f"parse data exception:{str(e)}") + return {'err': ErrorCode.INVALID_PARAMETERS}, 301 + + if req_path is None or self.routes.get(req_path) is None: + return {'err': ErrorCode.NOT_FOUND}, 404 + + try: + handler = self.routes.get(req_path) + return handler(req), 200 + except Exception as e: + logger.error(f"route to '{req_path}' exception:{str(e)}") + logger.error(traceback.format_exc()) + return {'err': ErrorCode.FAILED}, 500 +``` + +### C + +```python +class Router: + def dispatch(self, http_request_str): + req_path = None + req = None + try: + http_request = json.loads(http_request_str) + req_path = http_request.get('path') + req = http_request.get('data') + except Exception as e: + logger.error(f"parse data exception:{str(e)}") + return {'err': ErrorCode.INVALID_PARAMETERS}, 200 + + if req_path is None or self.routes.get(req_path) is None: + return {'err': ErrorCode.NOT_FOUND}, 200 + + try: + handler = self.routes.get(req_path) + return handler(req), 200 + except Exception as e: + logger.error(f"route to '{req_path}' exception:{str(e)}") + logger.error(traceback.format_exc()) + return {'err': ErrorCode.FAILED}, 500 +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.json" index 947951402f1289f75ec45afd78a96963dee809f8..a8bc78ea524388b4521cd8123ece02b9a35e1ae7 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.json" @@ -1,16 +1,5 @@ { - "one_line": { - "'/kv/create': self.__create": [ - "'/kv/create': self.__remove" - ], - "ErrorCode.internal_ret_2_http(ret)": [ - "ret.internal_ret_2_http()" - ], - "self.validator.validate(req, ['key', 'value'])": [ - "self.validator.validate(req, ['key'])" - ] - }, - "source": "server.py", + "source": "server.md", "depends": [ "error_code.py", "store.py", @@ -18,5 +7,7 @@ "validator.py" ], "exercise_id": 152, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.md" new file mode 100644 index 0000000000000000000000000000000000000000..61ce752cf1c95e5b7593929e80765bdd1df4d2fa --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/server.md" @@ -0,0 +1,249 @@ +# Python Web 服务模拟器 + +综合使用前2节的ErrorCode、Router两个类,模拟一个 Web 服务,支持: + +1. 创建资源 +2. 删除资源 +3. 统计资源个数 + +那么,先创建一个资源,接着删除同一个资源,最后统计资源个数,总数应该为0 + +```python +# -*- coding: UTF-8 -*- +from error_code import ErrorCode +from store import Store +from router import Router +from validator import KeyValueValidator +import json +import logging +import traceback +logger = logging.getLogger(__name__) + +class App: + def __init__(self) -> None: + self.store = Store({}) + self.validator = KeyValueValidator() + + # 创建一个路由器,支持3个目标API + self.router = Router({ + '/': lambda req: self.__home(req), + '/kv/create': lambda req: self.__create(req), + '/kv/remove': lambda req: self.__remove(req), + '/kv/count': lambda req: self.__count(req), + }) + + def post(self, path, data): + '''HTTP POST方法模拟实现 + path: 请求路径 + data: 请求数据,使用JSON模拟 + ''' + # TODO(You): 请正确实现 post 方法,接受 API 请求 + + return resp, status_code + + def __home(self, req): + # 首页 + return {'err': ErrorCode.SUCCESS, 'result': "Welcome!"} + + def __create(self, req): + # 创建资源 + ret = self.validator.validate(req, ['key', 'value']) + if ret['err'] != ErrorCode.SUCCESS: + return ret + + return self.store.create(req['key'], req['value']) + + def __remove(self, req): + # 移除资源 + ret = self.validator.validate(req, ['key', 'condition']) + if ret['err'] != ErrorCode.SUCCESS: + return ret + + return self.store.remove(req['key'], req['condition']) + + def __count(self, req): + # 统计资源个数 + return self.store.count() + + +if __name__ == '__main__': + app = App() + + resp, status = app.post('/kv/create', { + 'key': 'test', + 'value': 1000, + }) + + resp, status = app.post('/kv/remove', { + 'key': 'test', + 'condition': 1000, + }) + + resp, status = app.post('/kv/count', {}) + assert status == 200 + assert json.loads(resp)['err'] == 'success' + assert json.loads(resp)['result'] == 0 +``` + +以下对 post 方法的实现,**正确并且最佳**的代码是? + +## template + +```python +from error_code import ErrorCode +from store import Store +from router import Router +from validator import KeyValueValidator + +import json +import logging +import traceback +logger = logging.getLogger(__name__) + + +class App: + def __init__(self) -> None: + self.store = Store({}) + self.validator = KeyValueValidator() + self.router = Router({ + '/': self.__home, + '/kv/create': self.__create, + '/kv/remove': self.__remove, + '/kv/count': self.__count, + }) + + def post(self, path, data): + http_request = { + 'path': path, + 'data': data + } + ret, status_code = self.router.dispatch(json.dumps(http_request)) + ErrorCode.internal_ret_2_http(ret) + resp = "" + try: + resp = json.dumps(ret) + except Exception as e: + logger.error("parse resp exception:%s", str(e)) + logger.error(traceback.format_exc()) + + return resp, status_code + + def __home(self, req): + return {'err': ErrorCode.SUCCESS, 'result': "Welcome!"} + + def __create(self, req): + ret = self.validator.validate(req, ['key', 'value']) + if ret['err'] != ErrorCode.SUCCESS: + return ret + + return self.store.create(req['key'], req['value']) + + def __remove(self, req): + ret = self.validator.validate(req, ['key', 'condition']) + if ret['err'] != ErrorCode.SUCCESS: + return ret + + return self.store.remove(req['key'], req['condition']) + + def __count(self, req): + return self.store.count() + + +if __name__ == '__main__': + app = App() + + resp, status = app.post('/kv/create', { + 'key': 'test', + 'value': 1000, + }) + + resp, status = app.post('/kv/remove', { + 'key': 'test', + 'condition': 1000, + }) + + resp, status = app.post('/kv/count', {}) + assert status == 200 + assert json.loads(resp)['err'] == 'success' + assert json.loads(resp)['result'] == 0 +``` + +## 答案 + +```python +class App: + ... + def post(self, path, data): + http_request = { + 'path': path, + 'data': data + } + ret, status_code = self.router.dispatch(json.dumps(http_request)) + ErrorCode.internal_ret_2_http(ret) + resp = "" + try: + resp = json.dumps(ret) + except Exception as e: + logger.error("parse resp exception:%s", str(e)) + logger.error(traceback.format_exc()) + return resp, status_code +``` + +## 选项 + +### A + +```python +class App: + ... + def post(self, path, data): + http_request = { + 'path': path, + 'data': data + } + ret, status_code = self.router.dispatch(http_request) + ErrorCode.internal_ret_2_http(ret) + resp = "" + try: + resp = json.dumps(ret) + except Exception as e: + logger.error("parse resp exception:%s", str(e)) + logger.error(traceback.format_exc()) + return resp, status_code +``` + +### B + +```python +class App: + ... + def post(self, path, data): + http_request = { + 'path': path, + 'data': data + } + ret, status_code = self.router.dispatch(json.dumps(http_request)) + resp = "" + try: + resp = json.dumps(ret) + except Exception as e: + logger.error("parse resp exception:%s", str(e)) + logger.error(traceback.format_exc()) + return resp, status_code +``` + +### C + +```python +class App: + ... + def post(self, path, data): + http_request = { + 'path': path, + 'data': data + } + ret, status_code = self.router.dispatch(json.dumps(http_request)) + ErrorCode.internal_ret_2_http(ret) + resp = json.dumps(ret) + return resp, status_code +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.json" index a2c641b15456579b199763d686ee381615b20632..79fa49b84e20d0202050f48a7a9a60a04e154609 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.json" @@ -1,19 +1,10 @@ { - "one_line": { - "self.records.get(key) is None": [ - "self.records.get(key) is not None" - ], - "'result': len(self.records.keys())": [ - "'result': len(self.records)" - ], - "self.records.get(key) == condition": [ - "self.records.get(key) != condition" - ] - }, - "source": "store.py", + "source": "store.md", "depends": [ "error_code.py" ], "exercise_id": 150, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.md" new file mode 100644 index 0000000000000000000000000000000000000000..f0b1ded57b705c217d3bb818bb7b3d1dc2b0ba59 --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/store.md" @@ -0,0 +1,187 @@ +# 状态存储 + +使用dict数据结构,实现创建、删除、查询总数存储接口,创建/删除同一个资源后,总是应该为0 + +```python +# -*- coding: UTF-8 -*- +from error_code import ErrorCode +import logging +logger = logging.getLogger(__name__) + +class Store: + def __init__(self, config) -> None: + self.config = config + self.records = {} + + def __where(self, key, condition): + if condition is None: + return True + return self.records.get(key) == condition + + def create(self, key, value): + if self.records.get(key) is None: + self.records[key] = value + return {'err': ErrorCode.SUCCESS} + else: + return {'err': ErrorCode.ALREADY_EXIST} + + def update(self, key, value, condition=None): + if self.__where(key, condition): + self.records[key] = value + return {'err': ErrorCode.SUCCESS} + else: + return {'err': ErrorCode.NOT_FOUND} + + def remove(self, key, condition=None): + if self.__where(key, condition): + del self.records[key] + return {'err': ErrorCode.SUCCESS} + else: + return {'err': ErrorCode.NOT_FOUND} + + def count(self): + return {'err': ErrorCode.SUCCESS, 'result': len(self.records.keys())} + + def fetch(self, key, condition=None): + # TODO(You): 请实现一个根据key 和条件 condition 查找的方法 + + +if __name__ == '__main__': + store = Store({}) + ret = store.create("test", 100) + assert ret['err'] == ErrorCode.SUCCESS + + ret = store.remove("test") + assert ret['err'] == ErrorCode.SUCCESS + ret = store.count() + assert ret['err'] == ErrorCode.SUCCESS + assert ret['result'] == 0 +``` + +请选出下列能**正确**实现这一功能的选项。 + +## template + +```python +from error_code import ErrorCode +import logging +logger = logging.getLogger(__name__) + + +class Store: + def __init__(self, config) -> None: + self.config = config + self.records = {} + + def __where(self, key, condition): + if condition is None: + return True + return self.records.get(key) == condition + + def create(self, key, value): + if self.records.get(key) is None: + self.records[key] = value + return {'err': ErrorCode.SUCCESS} + else: + return {'err': ErrorCode.ALREADY_EXIST} + + def update(self, key, value, condition=None): + if self.__where(key, condition): + self.records[key] = value + return {'err': ErrorCode.SUCCESS} + else: + return {'err': ErrorCode.NOT_FOUND} + + def remove(self, key, condition=None): + if self.__where(key, condition): + del self.records[key] + return {'err': ErrorCode.SUCCESS} + else: + return {'err': ErrorCode.NOT_FOUND} + + def count(self): + return {'err': ErrorCode.SUCCESS, 'result': len(self.records.keys())} + + def fetch(self, key, condition=None): + if self.__where(key, condition): + result = self.records.get(key) + if result is None: + return {'err': ErrorCode.NOT_FOUND} + else: + return {'err': ErrorCode.SUCCESS, 'result': [result]} + else: + return {'err': ErrorCode.NOT_FOUND} + + +if __name__ == '__main__': + store = Store({}) + ret = store.create("test", 100) + assert ret['err'] == ErrorCode.SUCCESS + + ret = store.remove("test") + assert ret['err'] == ErrorCode.SUCCESS + ret = store.count() + assert ret['err'] == ErrorCode.SUCCESS + assert ret['result'] == 0 +``` + +## 答案 + +```python +class Store: + ... + def fetch(self, key, condition=None): + if self.__where(key, condition): + result = self.records.get(key) + if result is None: + return {'err': ErrorCode.NOT_FOUND} + else: + return {'err': ErrorCode.SUCCESS, 'result': [result]} + else: + return {'err': ErrorCode.NOT_FOUND} +``` + +## 选项 + +### A + +```python +class Store: + ... + def fetch(self, key, condition=None): + result = self.records.get(key) + if result is None: + return {'err': ErrorCode.NOT_FOUND} + else: + return {'err': ErrorCode.SUCCESS, 'result': [result]} + +``` + +### B + +```python +class Store: + ... + def fetch(self, key, condition=None): + if self.__where(key, condition): + result = self.records.get(key) + return {'err': ErrorCode.SUCCESS, 'result': [result]} + else: + return {'err': ErrorCode.NOT_FOUND} +``` + +### C + +```python +class Store: + ... + def fetch(self, key, condition=None): + if self.__where(key, condition): + result = self.records[key] + if result is None: + return {'err': ErrorCode.NOT_FOUND} + else: + return {'err': ErrorCode.SUCCESS, 'result': [result]} + else: + return {'err': ErrorCode.NOT_FOUND} +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.json" index f6c95e0b480f09cd42ee5184b7c02f613a451622..95e2fafe800dc9e6001f14b85ecaa0f292a38ca7 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.json" @@ -1,19 +1,10 @@ { - "one_line": { - "\"key\": {\"type\": \"string\"},": [ - "\"key\": {\"type\": \"number\"}," - ], - "\"required\": required": [ - "\"required\": req" - ], - "\"value\": {\"type\": \"number\"}": [ - "\"value\": {\"type\": [\"number\",\"string\"]}" - ] - }, - "source": "validator.py", + "source": "validator.md", "depends": [ "error_code.py" ], "exercise_id": 151, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.md" new file mode 100644 index 0000000000000000000000000000000000000000..ebf085b15efccbaf1ccffc7f53a5a665654820bf --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/1.Web\345\274\200\345\217\221\345\237\272\347\241\200\347\237\245\350\257\206/validator.md" @@ -0,0 +1,168 @@ +# 参数校验 + +使用 jsonschema 校验参数,key:字符串类型, value:数字, condition: 数字 + +```python +# -*- coding: UTF-8 -*- +from error_code import ErrorCode +from jsonschema import validate +import json +import logging +import traceback +logger = logging.getLogger(__name__) + +class KeyValueValidator: + def __init__(self) -> None: + pass + + def validate(self, req, required): + '''使用jsonschema校验参数 + req: 请求参数 + required: 必须要有的字段 + ''' + # TODO(You): 请正确配置 jsonschema + schema = ... + + try: + validate(instance=req, schema=schema) + return { + 'err': ErrorCode.SUCCESS + } + except Exception as e: + logger.error(f"validate exception:{str(e)}") + logger.error(traceback.format_exc()) + return { + 'err': ErrorCode.INVALID_PARAMETERS + } + + +if __name__ == '__main__': + v = KeyValueValidator() + + ret = v.validate({'key': "test", 'value': 100}, ['key', 'value']) + assert ret['err'] == ErrorCode.SUCCESS + ret = v.validate({'key': "test"}, ['key', 'value']) + assert ret['err'] == ErrorCode.INVALID_PARAMETERS +``` + +以下对方法 validate 里 jsonschema 配置正确的是? + +## template + +```python +from error_code import ErrorCode +from jsonschema import validate +import json +import logging +import traceback +logger = logging.getLogger(__name__) + + +class KeyValueValidator: + def __init__(self) -> None: + pass + + def validate(self, req, required): + schema = { + "type": "object", + "properties": { + "key": {"type": "string"}, + "value": {"type": "number"}, + "condition": {"type": "number"}, + }, + "required": required + } + + try: + validate(instance=req, schema=schema) + return { + 'err': ErrorCode.SUCCESS + } + except Exception as e: + logger.error(f"validate exception:{str(e)}") + logger.error(traceback.format_exc()) + return { + 'err': ErrorCode.INVALID_PARAMETERS + } + + +if __name__ == '__main__': + v = KeyValueValidator() + + ret = v.validate({'key': "test", 'value': 100}, ['key', 'value']) + assert ret['err'] == ErrorCode.SUCCESS + ret = v.validate({'key': "test"}, ['key', 'value']) + assert ret['err'] == ErrorCode.INVALID_PARAMETERS +``` + +## 答案 + +```python +class KeyValueValidator: + ... + def validate(self, req, required): + schema = { + "type": "object", + "properties": { + "key": {"type": "string"}, + "value": {"type": "number"}, + "condition": {"type": "number"}, + }, + "required": required + } + ... +``` + +## 选项 + +### A + +```python +class KeyValueValidator: + ... + def validate(self, req, required): + schema = { + "properties": { + "key": {"type": "string"}, + "value": {"type": "number"}, + "condition": {"type": "number"}, + }, + "required": required + } + ... +``` + +### B + +```python +class KeyValueValidator: + ... + def validate(self, req, required): + schema = { + "type": "object", + "properties": { + "key": {"type": "string"}, + "value": {"type": "number"}, + "condition": {"type": "number"}, + } + } + ... +``` + +### C + +```python +class KeyValueValidator: + ... + def validate(self, req, required): + schema = { + "type": "object", + "properties": { + "key": {"type": "number"}, + "value": {"type": "number"}, + "condition": {"type": "number"}, + }, + "required": required + } + ... +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.json" index 29d94be22c1c72d48d25d6f67ea12ed874764cfb..cfd0ba2041b2d7f4a1777f3c77aa4f6dc65c55d1 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.json" @@ -1,20 +1,8 @@ { - "one_line": { - "pip install django": [ - "pip install dango" - ], - "django-admin": [ - "django_admin" - ], - "startproject": [ - "project" - ], - "startapp": [ - "app" - ] - }, - "source": "howto.py", + "source": "howto.md", "depends": [], "exercise_id": 104, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.md" new file mode 100644 index 0000000000000000000000000000000000000000..70d274c640b0f1f8261b589846de16b12337bd55 --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/2. Django/howto.md" @@ -0,0 +1,70 @@ +# Python Django 框架 + +自己动手,丰衣足食,打印 Django 教程的入口信息 + +```python +# -*- coding: UTF-8 -*- +if __name__ == '__main__': + + # TODO(You): 请在此添加创建django项目和初始化目录的命令 + create_django = ... + init_django = ... + + installs = [ + "安装:pip install django", + f"创建django项目命令:{create_django}", + "进入目录:cd projectName", + f"初始化django项目命令:{init_django}", + "进一步查看教程:https://docs.djangoproject.com/zh-hans/3.2/" + ] + for step in installs: + print(step) +``` + +以下正确赋值创建django项目和初始化目录的命令的代码是? + +## template + +```python + +if __name__ == '__main__': + installs = [ + "安装:pip install django", + "初始化:django-admin startproject projectName", + "进入目录:cd projectName", + "初始化:python manage.py startapp projectApp", + "进一步查看教程:https://docs.djangoproject.com/zh-hans/3.2/" + ] + for step in installs: + print(step) +``` + +## 答案 + +```python +create_django = 'django-admin startproject projectName' +init_django = 'python manage.py startapp projectApp' +``` + +## 选项 + +### A + +```python +create_django = 'django startproject projectName' +init_django = 'django startapp projectApp' +``` + +### B + +```python +create_django = 'python manage.py startproject projectName' +init_django = 'python manage.py startapp projectApp' +``` + +### C + +```python +create_django = 'django-admin startproject projectName' +init_django = 'django-admin startapp projectApp' +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.json" index 96678aed921791a08cfe22abfeaaaf836842cee5..211ab7bde9cd7c1696ab375df96c6c50d569bf8a 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.json" @@ -1,18 +1,8 @@ { - "one_line": { - "start()": [ - "listen()", - "accept()" - ], - "app.listen(8000)": [ - "app.listen(80)" - ], - "self.write": [ - "self.read" - ] - }, - "source": "hello.py", + "source": "hello.md", "depends": [], "exercise_id": 106, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.md" new file mode 100644 index 0000000000000000000000000000000000000000..212e40ff989a3aa1469e76283e0b69aa473957ac --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/3. Tornado/hello.md" @@ -0,0 +1,86 @@ +# Python tornado 框架 + +tornado 服务,极简路由 + +```python +# -*- coding: UTF-8 -*- +import tornado.web +import tornado.ioloop + +class IndexHandler(tornado.web.RequestHandler): + """主路由处理类""" + + def get(self): + """对应http的get请求方式""" + # TODO(You): 实现Tornado get 方法 + +if __name__ == "__main__": + app = tornado.web.Application([ + (r"/", IndexHandler), + ]) + app.listen(8000) + + print("* Tornado Web Server 已在 8000 端口启动。") + print("* 请在浏览器里输入 127.0.0.1:8000") + tornado.ioloop.IOLoop.current().start() +``` + +请选出下列能**正确**实现这一功能的选项。 + +## template + +```python +import tornado.web +import tornado.ioloop + + +class IndexHandler(tornado.web.RequestHandler): + """主路由处理类""" + + def get(self): + """对应http的get请求方式""" + self.write("Hello Tornado!") + +if __name__ == "__main__": + app = tornado.web.Application([ + (r"/", IndexHandler), + ]) + app.listen(8000) + print("* Tornado Web Server 已在 8000 端口启动。") + print("* 请在浏览器里输入 127.0.0.1:8000") + tornado.ioloop.IOLoop.current().start() +``` + +## 答案 + +```python +class IndexHandler(tornado.web.RequestHandler): + def get(self): + self.write("Hello Tornado!") +``` + +## 选项 + +### A + +```python +class IndexHandler(tornado.web.RequestHandler): + def get(self): + return "Hello Tornado!" +``` + +### B + +```python +class IndexHandler(tornado.web.RequestHandler): + def get(self): + self.write("Hello Tornado!", 200) +``` + +### C + +```python +class IndexHandler(tornado.web.RequestHandler): + def get(self): + self.write_error("Hello Tornado!", 500) +``` diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.json" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.json" index bbbe0053640989dfbaa1e30dfe19e19487b3bc3a..3dfd5431840c3a7de21df848d8a89ed36f36afbc 100644 --- "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.json" +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.json" @@ -1,17 +1,8 @@ { - "one_line": { - "pywsgi.WSGIServer((host, port), app)": [ - "pywsgi.WSGIServer(host, port, app)" - ], - "server.serve_forever()": [ - "server.start()" - ], - "methods=['GET']": [ - "methods=[GET]" - ] - }, - "source": "server.py", + "source": "server.md", "depends": [], "exercise_id": 212, - "type": "code_options" + "type": "code_options", + "author": "huanhuilong", + "notebook_enable": true } \ No newline at end of file diff --git "a/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.md" "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.md" new file mode 100644 index 0000000000000000000000000000000000000000..efca179fd20ebc8b2ed7cccbe3e08e908b9f8e46 --- /dev/null +++ "b/data/2.python\344\270\255\351\230\266/2.Web\345\272\224\347\224\250\345\274\200\345\217\221/4. Flask/server.md" @@ -0,0 +1,119 @@ +# Flask简单使用 + +用flask启动web服务,响应根页面HTTP GET请求:`'/'`,返回`"

Hello World!

"` + +```python +# -*- coding: UTF-8 -*- +import sys +from flask import Flask +from flask_cors import CORS +from gevent import pywsgi, monkey + +monkey.patch_all() + +app = Flask( + __name__, + static_folder='web', + static_url_path='' +) + +def after_request(resp): + resp.headers['Access-Control-Allow-Origin'] = '*' + return resp + +app.after_request(after_request) +app.config['JSON_AS_ASCII'] = False +CORS(app, supports_credentials=True) + +# TODO(You): 请在此添加Flask API响应 + +if __name__ == '__main__': + host = '127.0.0.1' + port = 1024 + print('@启动服务...') + print("@本地调试:http://{}:{}".format(host, port)) + if len(sys.argv) > 1 and sys.argv[1] == 'debug': + app.run(host=host, port=port) + else: + server = pywsgi.WSGIServer((host, port), app) + server.serve_forever() +``` + +请选出下列能**正确**实现这一功能的选项。 + +## template + +```python +import sys +from flask import Flask +from flask_cors import CORS +from gevent import pywsgi, monkey + +monkey.patch_all() + +app = Flask( + __name__, + static_folder='web', + static_url_path='' +) + + +def after_request(resp): + resp.headers['Access-Control-Allow-Origin'] = '*' + return resp + + +app.after_request(after_request) +app.config['JSON_AS_ASCII'] = False +CORS(app, supports_credentials=True) + + +@app.route('/', methods=['GET']) +def home(): + return "

Hello World!

" + +if __name__ == '__main__': + host = '127.0.0.1' + port = 1024 + print('@启动服务...') + print("@本地调试:http://{}:{}".format(host, port)) + if len(sys.argv) > 1 and sys.argv[1] == 'debug': + app.run(host=host, port=port) + else: + server = pywsgi.WSGIServer((host, port), app) + server.serve_forever() +``` + +## 答案 + +```python +@app.route('/', methods=['GET']) +def home(): + return "

Hello World!

" +``` + +## 选项 + +### A + +```python +@app.route('/', methods=['POST']) +def home(): + return "

Hello World!

" +``` + +### B + +```python +@app.route('/', methods=['GET']) +def home(): + return "Hello World!" +``` + +### C + +```python +@app.route('/') +def home(): + return "

Hello World!

" +``` diff --git a/main.py b/main.py index 3242314687bdabdeb2f8bcf29a2c4f537c1db13f..93af896ccc1c486d09218a779527399498546a80 100644 --- a/main.py +++ b/main.py @@ -9,5 +9,5 @@ if __name__ == '__main__': # walker = TreeWalker("data", "python", "python") # walker.walk() - md = MDWalker('data/2.python中阶/1.基本技能') + md = MDWalker('data/2.python中阶/2.Web应用开发') md.walk()