摘要:到這里,我們的服務(wù)的框架已經(jīng)搭建完成,并且測(cè)試服務(wù)器也跑起來了。上面的代碼也就可以修改為再次運(yùn)行我們的測(cè)試服務(wù)器,就可以返現(xiàn)返回值為格式了。我們先來完成利用來檢查返回值的代碼方法的第一個(gè)參數(shù)表示返回值的類型這樣就完成了的返回值檢查了。
上一篇文章說到,我們將以實(shí)例的形式來繼續(xù)講述這個(gè)API服務(wù)的開發(fā)知識(shí),這里會(huì)使用Pecan和WSME兩個(gè)庫。
設(shè)計(jì)REST API要開發(fā)REST API服務(wù),我們首先需要設(shè)計(jì)一下這個(gè)服務(wù)。設(shè)計(jì)包括要實(shí)現(xiàn)的功能,以及接口的具體規(guī)范。我們這里要實(shí)現(xiàn)的是一個(gè)簡(jiǎn)單的用戶管理接口,包括增刪改查等功能。如果讀者對(duì)REST API不熟悉,可以先從Wiki頁面了解一下。
另外,為了方便大家閱讀和理解,本系列的代碼會(huì)放在github上,diabloneo/webdemo。
Version of REST API在OpenStack的項(xiàng)目中,都是在URL中表明這個(gè)API的版本號(hào)的,比如Keystone的API會(huì)有/v2.0和/v3的前綴,表明兩個(gè)不同版本的API;Magnum項(xiàng)目目前的API則為v1版本。因?yàn)槲覀兊膚ebdemo項(xiàng)目才剛剛開始,所以我們也把我們的API版本設(shè)置為v1,下文會(huì)說明怎么實(shí)現(xiàn)這個(gè)version號(hào)的設(shè)置。
REST API of Users我們將要設(shè)計(jì)一個(gè)管理用戶的API,這個(gè)和Keystone的用戶管理的API差不多,這里先列出每個(gè)API的形式,以及簡(jiǎn)要的內(nèi)容說明。這里我們會(huì)把上面提到的version號(hào)也加入到URL path中,讓讀者能更容易聯(lián)系起來。
GET /v1/users 獲取所有用戶的列表。
POST /v1/users 創(chuàng)建一個(gè)用戶
GET /v1/users/
獲取一個(gè)特定用戶的詳細(xì)信息。 PUT /v1/users/
修改一個(gè)用戶的詳細(xì)信息。 DELETE /v1/users/
刪除一個(gè)用戶。
這些就是我們要實(shí)現(xiàn)的用戶管理的API了。其中,
In [5]: import uuid In [6]: print uuid.uuid4() adb92482-baab-4832-84bc-f842f3eabd66 In [7]: print uuid.uuid4().hex 29520c88de6b4c76ae8deb48db0a71e7
因?yàn)槭莻€(gè)demo,所以我們?cè)O(shè)置一個(gè)用戶包含的信息會(huì)比較簡(jiǎn)單,只包含name和age。
使用Pecan搭建API服務(wù)的框架接下來就要開始編碼工作了。首先要把整個(gè)服務(wù)的框架搭建起來。我們會(huì)在軟件包管理這篇文件中的代碼基礎(chǔ)上繼續(xù)我們的demo(所有這些代碼在github的倉庫里都能看到)。
代碼目錄結(jié)構(gòu)一般來說,OpenStack項(xiàng)目中,使用Pecan來開發(fā)API服務(wù)時(shí),都會(huì)在代碼目錄下有一個(gè)專門的API目錄,用來保存API相關(guān)的代碼。比如Magnum項(xiàng)目的magnum/api,或者Ceilometer項(xiàng)目的ceilometer/api等。我們的代碼也遵守這個(gè)規(guī)范,讓我們直接來看下我們的代碼目錄結(jié)構(gòu)(#后面的表示注釋):
? ~/programming/python/webdemo/webdemo/api git:(master) ? $ tree . . ├── app.py # 這個(gè)文件存放WSGI application的入口 ├── config.py # 這個(gè)文件存放Pecan的配置 ├── controllers/ # 這個(gè)目錄用來存放Pecan控制器的代碼 ├── hooks.py # 這個(gè)文件存放Pecan的hooks代碼(本文中用不到) └── __init__.py
這個(gè)在API服務(wù)(3)這篇文章中已經(jīng)說明過了。
先讓我們的服務(wù)跑起來為了后面更好的開發(fā),我們需要先讓我們的服務(wù)在本地跑起來,這樣可以方便自己做測(cè)試,看到代碼的效果。不過要做到這點(diǎn),還是有些復(fù)雜的。
必要的代碼首先,先創(chuàng)建config.py文件的內(nèi)容:
app = { "root": "webdemo.api.controllers.root.RootController", "modules": ["webdemo.api"], "debug": False, }
就是包含了Pecan的最基本配置,其中指定了root controller的位置。然后看下app.py文件的內(nèi)容,主要就是讀取config.py中的配置,然后創(chuàng)建一個(gè)WSGI application:
import pecan from webdemo.api import config as api_config def get_pecan_config(): filename = api_config.__file__.replace(".pyc", ".py") return pecan.configuration.conf_from_file(filename) def setup_app(): config = get_pecan_config() app_conf = dict(config.app) app = pecan.make_app( app_conf.pop("root"), logging=getattr(config, "logging", {}), **app_conf ) return app
然后,我們至少還需要實(shí)現(xiàn)一下root controller,也就是webdemo/api/controllers/root.py這個(gè)文件中的RootController類:
from pecan import rest from wsme import types as wtypes import wsmeext.pecan as wsme_pecan class RootController(rest.RestController): @wsme_pecan.wsexpose(wtypes.text) def get(self): return "webdemo"本地測(cè)試服務(wù)器
為了繼續(xù)開放的方便,我們要先創(chuàng)建一個(gè)Python腳本,可以啟動(dòng)一個(gè)單進(jìn)程的API服務(wù)。這個(gè)腳本會(huì)放在webdemo/cmd/目錄下,名稱是api.py(這目錄和腳本名稱也是慣例),來看看我們的api.py吧:
from wsgiref import simple_server from webdemo.api import app def main(): host = "0.0.0.0" port = 8080 application = app.setup_app() srv = simple_server.make_server(host, port, application) srv.serve_forever() if __name__ == "__main__": main()運(yùn)行測(cè)試服務(wù)器的環(huán)境
要運(yùn)行這個(gè)測(cè)試服務(wù)器,首先需要安裝必要的包,并且設(shè)置正確的路徑。在后面的文章中,我們將會(huì)知道,這個(gè)可以通過tox這個(gè)工具來實(shí)現(xiàn)。現(xiàn)在,我們先做個(gè)簡(jiǎn)單版本的,就是手動(dòng)創(chuàng)建這個(gè)運(yùn)行環(huán)境。
首先,完善一下requirements.txt這個(gè)文件,包含我們需要的包:
pbr<2.0,>=0.11 pecan WSME
然后,我們手動(dòng)創(chuàng)建一個(gè)virtualenv環(huán)境,并且安裝requirements.txt中要求的包:
? ~/programming/python/webdemo git:(master) ? $ virtualenv .venv New python executable in .venv/bin/python Installing setuptools, pip, wheel...done. ? ~/programming/python/webdemo git:(master) ? $ source .venv/bin/activate (.venv)? ~/programming/python/webdemo git:(master) ? $ pip install -r requirement.txt ... Successfully installed Mako-1.0.3 MarkupSafe-0.23 WSME-0.8.0 WebOb-1.5.1 WebTest-2.0.20 beautifulsoup4-4.4.1 logutils-0.3.3 netaddr-0.7.18 pbr-1.8.1 pecan-1.0.3 pytz-2015.7 simplegeneric-0.8.1 singledispatch-3.4.0.3 six-1.10.0 waitress-0.8.10啟動(dòng)我們的服務(wù)
啟動(dòng)服務(wù)需要技巧,因?yàn)槲覀兊膚ebdemo還沒有安裝到系統(tǒng)的Python路徑中,也不在上面創(chuàng)建virtualenv環(huán)境中,所以我們需要通過指定PYTHONPATH這個(gè)環(huán)境變量來為Python程序增加庫的查找路徑:
(.venv)? ~/programming/python/webdemo git:(master) ? $ PYTHONPATH=. python webdemo/cmd/api.py
現(xiàn)在測(cè)試服務(wù)器已經(jīng)起來了,可以通過瀏覽器訪問http://localhost:8080/ 這個(gè)地址來查看結(jié)果。(你可能會(huì)發(fā)現(xiàn),返回的是XML格式的結(jié)果,而我們想要的是JSON格式的。這個(gè)是WSME的問題,我們后面再來處理)。
到這里,我們的REST API服務(wù)的框架已經(jīng)搭建完成,并且測(cè)試服務(wù)器也跑起來了。
用戶管理API的實(shí)現(xiàn)現(xiàn)在我們來實(shí)現(xiàn)我們?cè)诘谝徽略O(shè)計(jì)的API。這里先說明一下:我們會(huì)直接使用Pecan的RestController來實(shí)現(xiàn)REST API,這樣可以不用為每個(gè)接口指定接受的method。
讓API返回JSON格式的數(shù)據(jù)現(xiàn)在,所有的OpenStack項(xiàng)目的REST API的返回格式都是使用JSON標(biāo)準(zhǔn),所以我們也要這么做。那么有什么辦法能夠讓W(xué)SME框架返回JSON數(shù)據(jù)呢?可以通過設(shè)置wsmeext.pecan.wsexpose()的rest_content_types參數(shù)來是先。這里,我們借鑒一段Magnum項(xiàng)目中的代碼,把這段代碼存放在文件webdemo/api/expose.py中:
import wsmeext.pecan as wsme_pecan def expose(*args, **kwargs): """Ensure that only JSON, and not XML, is supported.""" if "rest_content_types" not in kwargs: kwargs["rest_content_types"] = ("json",) return wsme_pecan.wsexpose(*args, **kwargs)
這樣我們就封裝了自己的expose裝飾器,每次都會(huì)設(shè)置響應(yīng)的content-type為JSON。上面的root controller代碼也就可以修改為:
from pecan import rest from wsme import types as wtypes from webdemo.api import expose class RootController(rest.RestController): @expose.expose(wtypes.text) def get(self): return "webdemo"
再次運(yùn)行我們的測(cè)試服務(wù)器,就可以返現(xiàn)返回值為JSON格式了。
實(shí)現(xiàn) GET /v1這個(gè)其實(shí)就是實(shí)現(xiàn)v1這個(gè)版本的API的路徑前綴。在Pecan的幫助下,我們很容易實(shí)現(xiàn)這個(gè),只要按照如下兩步做即可:
先實(shí)現(xiàn)v1這個(gè)controller
把v1 controller加入到root controller中
按照OpenStack項(xiàng)目的規(guī)范,我們會(huì)先建立一個(gè)webdemo/api/controllers/v1/目錄,然后將v1 controller放在這個(gè)目錄下的一個(gè)文件中,假設(shè)我們就放在v1/controller.py文件中,效果如下:
from pecan import rest from wsme import types as wtypes from webdemo.api import expose class V1Controller(rest.RestController): @expose.expose(wtypes.text) def get(self): return "webdemo v1controller"
然后把這個(gè)controller加入到root controller中:
... from webdemo.api.controllers.v1 import controller as v1_controller from webdemo.api import expose class RootController(rest.RestController): v1 = v1_controller.V1Controller() @expose.expose(wtypes.text) def get(self): return "webdemo"
此時(shí),你訪問http://localhost:8080/v1就可以看到結(jié)果了。
實(shí)現(xiàn) GET /v1/users 添加users controller這個(gè)API就是返回所有的用戶信息,功能很簡(jiǎn)單。首先要添加users controller到上面的v1 controller中。為了不影響閱讀體驗(yàn),這里就不貼代碼了,請(qǐng)看github上的示例代碼。
使用WSME來規(guī)范API的響應(yīng)值上篇文章中,我們已經(jīng)提到了WSME可以用來規(guī)范API的請(qǐng)求和響應(yīng)的值,這里我們就要用上它。首先,我們要參考OpenStack的慣例來設(shè)計(jì)這個(gè)API的返回值:
{ "users": [ { "name": "Alice", "age": 30 }, { "name": "Bob", "age": 40 } ] }
其中users是一個(gè)列表,列表中的每個(gè)元素都是一個(gè)user。那么,我們要如何使用WSME來規(guī)范我們的響應(yīng)值呢?答案就是使用WSME的自定義類型。我們可以利用WSME的類型功能定義出一個(gè)user類型,然后再定義一個(gè)user的列表類型。最后,我們就可以使用上面的expose方法來規(guī)定這個(gè)API返回的是一個(gè)user的列表類型。
定義user類型和user列表類型這里我們需要用到WSME的Complex types的功能,請(qǐng)先看一下文檔Types。簡(jiǎn)單說,就是我們可以把WSME的基本類型組合成一個(gè)復(fù)雜的類型。我們的類型需要繼承自wsme.types.Base這個(gè)類。因?yàn)槲覀冊(cè)诒疚闹粫?huì)實(shí)現(xiàn)一個(gè)user相關(guān)的API,所以這里我們把所有的代碼都放在webdemo/api/controllers/v1/users.py文件中。來看下和user類型定義相關(guān)的部分:
from wsme import types as wtypes class User(wtypes.Base): name = wtypes.text age = int class Users(wtypes.Base): users = [User]
這里我們定義了class User,表示一個(gè)用戶信息,包含兩個(gè)字段,name是一個(gè)文本,age是一個(gè)整型。class Users表示一組用戶信息,包含一個(gè)字段users,是一個(gè)列表,列表的元素是上面定義的class User。完成這些定義后,我們就使用WSME來檢查我們的API是否返回了合格的值;另一方面,只要我們的API返回了這些類型,那么就能通過WSME的檢查。我們先來完成利用WSME來檢查API返回值的代碼:
class UsersController(rest.RestController): # expose方法的第一個(gè)參數(shù)表示返回值的類型 @expose.expose(Users) def get(self): pass
這樣就完成了API的返回值檢查了。
實(shí)現(xiàn)API邏輯我們現(xiàn)在來完成API的邏輯部分。不過為了方便大家理解,我們直接返回一個(gè)寫好的數(shù)據(jù),就是上面貼出來的那個(gè)。
class UsersController(rest.RestController): @expose.expose(Users) def get(self): user_info_list = [ { "name": "Alice", "age": 30, }, { "name": "Bob", "age": 40, } ] users_list = [User(**user_info) for user_info in user_info_list] return Users(users=users_list)
代碼中,會(huì)先根據(jù)user信息生成User實(shí)例的列表users_list,然后再生成Users實(shí)例。此時(shí),重啟測(cè)試服務(wù)器后,你就可以從瀏覽器訪問http://localhost:8080/v1/users,就能看到結(jié)果了。
實(shí)現(xiàn) POST /v1/users這個(gè)API會(huì)接收用戶上傳的一個(gè)JSON格式的數(shù)據(jù),然后打印出來(實(shí)際中一般是存到數(shù)據(jù)庫之類的),要求用戶上傳的數(shù)據(jù)符合User類型的規(guī)范,并且返回的狀態(tài)碼為201。代碼如下:
class UsersController(rest.RestController): @expose.expose(None, body=User, status_code=201) def post(self, user): print user
可以使用curl程序來測(cè)試:
~/programming/python/webdemo git:(master) ? $ curl -X POST http://localhost:8080/v1/users -H "Content-Type: application/json" -d "{"name": "Cook", "age": 50}" -v * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > POST /v1/users HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.43.0 > Accept: */* > Content-Type: application/json > Content-Length: 27 > * upload completely sent off: 27 out of 27 bytes * HTTP 1.0, assume close after body < HTTP/1.0 201 Created < Date: Mon, 16 Nov 2015 15:18:24 GMT < Server: WSGIServer/0.1 Python/2.7.10 < Content-Length: 0 < * Closing connection 0
同時(shí),服務(wù)器上也會(huì)打印出:
127.0.0.1 - - [16/Nov/2015 23:16:28] "POST /v1/users HTTP/1.1" 201 0
我們用3行代碼就實(shí)現(xiàn)了這個(gè)POST的邏輯。現(xiàn)在來說明一下這里的秘密。expose裝飾器的第一個(gè)參數(shù)表示這個(gè)方法沒有返回值;第三個(gè)參數(shù)表示這個(gè)API的響應(yīng)狀態(tài)碼是201,如果不加這個(gè)參數(shù),在沒有返回值的情況下,默認(rèn)會(huì)返回204。第二個(gè)參數(shù)要說明一下,這里用的是body=User,你也可以直接寫User。使用body=User這種形式,你可以直接發(fā)送符合User規(guī)范的JSON字符串;如果是用expose(None, User, status_code=201)那么你需要發(fā)送下面這樣的數(shù)據(jù):
{ "user": {"name": "Cook", "age": 50} }
你可以自己測(cè)試一下區(qū)別。要更多的了解本節(jié)提到的expose參數(shù),請(qǐng)參考WSM文檔Functions。
最后,你接收到一個(gè)創(chuàng)建用戶請(qǐng)求時(shí),一般會(huì)為這個(gè)用戶分配一個(gè)id。本文前面已經(jīng)提到了OpenStack項(xiàng)目中一般使用UUID。你可以修改一下上面的邏輯,為每個(gè)用戶分配一個(gè)UUID。
實(shí)現(xiàn) GET /v1/users/要實(shí)現(xiàn)這個(gè)API,需要兩個(gè)步驟:
在UsersController中解析出
在UserController中實(shí)現(xiàn)get()方法。
使用_lookup()方法Pecan的_lookup()方法是controller中的一個(gè)特殊方法,Pecan會(huì)在特定的時(shí)候調(diào)用這個(gè)方法來實(shí)現(xiàn)更靈活的URL路由。Pecan還支持用戶實(shí)現(xiàn)_default()和_route()方法。這些方法的具體說明,請(qǐng)閱讀Pecan的文檔:routing。
我們這里只用到_lookup()方法,這個(gè)方法會(huì)在controller中沒有其他方法可以執(zhí)行且沒有_default()方法的時(shí)候執(zhí)行。比如上面的UsersController中,沒有定義/v1/users/
_lookup()方法需要返回一個(gè)元組,元組的第一個(gè)元素是下一個(gè)controller的實(shí)例,第二個(gè)元素是URL path中剩余的部分。
在這里,我們就需要在_lookup()方法中解析出UUID的部分并傳遞給新的controller作為新的參數(shù),并且返回剩余的URL path。來看下代碼:
class UserController(rest.RestController): def __init__(self, user_id): self.user_id = user_id class UsersController(rest.RestController): @pecan.expose() def _lookup(self, user_id, *remainder): return UserController(user_id), remainder
_lookup()方法的形式為_lookup(self, user_id, *remainder),意思就是會(huì)把/v1/users/
實(shí)現(xiàn)前,我們要先修改一下我們返回的數(shù)據(jù),里面需要增加一個(gè)id字段。對(duì)應(yīng)的User定義如下:
class User(wtypes.Base): id = wtypes.text name = wtypes.text age = int
現(xiàn)在,完整的UserController代碼如下:
class UserController(rest.RestController): def __init__(self, user_id): self.user_id = user_id @expose.expose(User) def get(self): user_info = { "id": self.user_id, "name": "Alice", "age": 30, } return User(**user_info)
使用curl來檢查一下效果:
? ~/programming/python/webdemo git:(master) ? $ curl http://localhost:8080/v1/users/29520c88de6b4c76ae8deb48db0a71e7 {"age": 30, "id": "29520c88de6b4c76ae8deb48db0a71e7", "name": "Alice"}定義WSME類型的技巧
你可能會(huì)有疑問:這里我們修改了User類型,增加了一個(gè)id字段,那么前面實(shí)現(xiàn)的POST /v1/users會(huì)不會(huì)失效呢?你可以自己測(cè)試一下。(答案是不會(huì),因?yàn)檫@個(gè)類型里的字段都是可選的)。這里順便講兩個(gè)技巧。
如何設(shè)置一個(gè)字段為強(qiáng)制字段
像下面這樣做就可以了(你可以測(cè)試一下,改成這樣后,不傳遞id的POST /v1/users會(huì)失敗):
class User(wtypes.Base): id = wtypes.wsattr(wtypes.text, mandatory=True) name = wtypes.text age = int
如何檢查一個(gè)可選字段的值是否存在
檢查這個(gè)值是否為None是肯定不行的,需要檢查這個(gè)值是否為wsme.Unset。
實(shí)現(xiàn) PUT /v1/users/這個(gè)和上一個(gè)API一樣,不過_lookup()方法已經(jīng)實(shí)現(xiàn)過了,直接添加方法到UserController中即可:
class UserController(rest.RestController): @expose.expose(User, body=User) def put(self, user): user_info = { "id": self.user_id, "name": user.name, "age": user.age + 1, } return User(**user_info)
通過curl來測(cè)試:
? ~/programming/python/webdemo git:(master) ? $ curl -X PUT http://localhost:8080/v1/users/29520c88de6b4c76ae8deb48db0a71e7 -H "Content-Type: application/json" -d "{"name": "Cook", "age": 50}" {"age": 51, "id": "29520c88de6b4c76ae8deb48db0a71e7", "name": "Cook"}%實(shí)現(xiàn) DELETE /v1/users/
同上,沒有什么新的內(nèi)容:
class UserController(rest.RestController): @expose.expose() def delete(self): print "Delete user_id: %s" % self.user_id總結(jié)
到此為止,我們已經(jīng)完成了我們的API服務(wù)了,雖然沒有實(shí)際的邏輯,但是本文搭建起來的框架也是OpenStack中API服務(wù)的一個(gè)常用框架,很多大項(xiàng)目的API服務(wù)代碼都和我們的webdemo長(zhǎng)得差不多。最后再說一下,本文的代碼在github上托管著:diabloneo/webdemo。
現(xiàn)在我們已經(jīng)了解了包管理和API服務(wù)了,那么接下來就要開始數(shù)據(jù)庫相關(guān)的操作了。大部分OpenStack的項(xiàng)目都是使用非常著名的sqlalchemy庫來實(shí)現(xiàn)數(shù)據(jù)庫操作的,本系列接下來的文章就是要來說明數(shù)據(jù)庫的相關(guān)知識(shí)和應(yīng)用。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/44182.html
摘要:通過,也就是通過各個(gè)項(xiàng)目提供的來使用各個(gè)服務(wù)的功能。通過使用的方式是由各個(gè)服務(wù)自己實(shí)現(xiàn)的,比如負(fù)責(zé)計(jì)算的項(xiàng)目實(shí)現(xiàn)了計(jì)算相關(guān)的,負(fù)責(zé)認(rèn)證的項(xiàng)目實(shí)現(xiàn)了認(rèn)證和授權(quán)相關(guān)的。的服務(wù)都是使用的方式來部署的。 使用OpenStack服務(wù)的方式 OpenStack項(xiàng)目作為一個(gè)IaaS平臺(tái),提供了三種使用方式: 通過Web界面,也就是通過Dashboard(面板)來使用平臺(tái)上的功能。 通過命令行,也就...
摘要:在實(shí)際項(xiàng)目中,這么做肯定是不行的實(shí)際項(xiàng)目中不會(huì)使用內(nèi)存數(shù)據(jù)庫,這種數(shù)據(jù)庫一般只是在單元測(cè)試中使用。接下來,我們將會(huì)了解中單元測(cè)試的相關(guān)知識(shí)。 在上一篇文章,我們介紹了SQLAlchemy的基本概念,也介紹了基本的使用流程。本文我們結(jié)合webdemo這個(gè)項(xiàng)目來介紹如何在項(xiàng)目中使用SQLAlchemy。另外,我們還會(huì)介紹數(shù)據(jù)庫版本管理的概念和實(shí)踐,這也是OpenStack每個(gè)項(xiàng)目都需要做的...
摘要:本文將進(jìn)入單元測(cè)試的部分,這也是基礎(chǔ)知識(shí)中最后一個(gè)大塊。本文將重點(diǎn)講述和中的單元測(cè)試的生態(tài)環(huán)境。另外,在中指定要運(yùn)行的單元測(cè)試用例的完整語法是。中使用模塊管理單元測(cè)試用例。每個(gè)項(xiàng)目的單元測(cè)試代碼結(jié)構(gòu)可 本文將進(jìn)入單元測(cè)試的部分,這也是基礎(chǔ)知識(shí)中最后一個(gè)大塊。本文將重點(diǎn)講述Python和OpenStack中的單元測(cè)試的生態(tài)環(huán)境。 單元測(cè)試的重要性 github上有個(gè)人畫了一些不同語言的學(xué)...
摘要:不幸的是,在軟件包管理十分混亂,至少歷史上十分混亂。的最大改進(jìn)是將函數(shù)的參數(shù)單獨(dú)放到一個(gè)的文件中這些成為包的元數(shù)據(jù)。基于的版本號(hào)管理。的版本推導(dǎo)這里重點(diǎn)說明一下基于的版本號(hào)管理這個(gè)功能。開發(fā)版本號(hào)的形式如下。 為什么寫這個(gè)系列 OpenStack是目前我所知的最大最復(fù)雜的基于Python項(xiàng)目。整個(gè)OpenStack項(xiàng)目包含了數(shù)十個(gè)主要的子項(xiàng)目,每個(gè)子項(xiàng)目所用到的庫也不盡相同。因此,對(duì)于...
摘要:從上面的例子可以看出,決定響應(yīng)類型的主要是傳遞給函數(shù)的參數(shù),我們看下函數(shù)的完整聲明參數(shù)用來指定返回值的模板,如果是就會(huì)返回內(nèi)容,這里可以指定一個(gè)文件,或者指定一個(gè)模板。用來做什么上面兩節(jié)已經(jīng)說明了可以比較好的處理請(qǐng)求中的參數(shù)以及控制返回值。 上一篇文章我們了解了一個(gè)巨啰嗦的框架:Paste + PasteDeploy + Routes + WebOb。后來OpenStack社區(qū)的人受不...
閱讀 1660·2021-11-12 10:35
閱讀 1611·2021-08-03 14:02
閱讀 2655·2019-08-30 15:55
閱讀 2024·2019-08-30 15:54
閱讀 735·2019-08-30 14:01
閱讀 2421·2019-08-29 17:07
閱讀 2246·2019-08-26 18:37
閱讀 3028·2019-08-26 16:51