From 5c49296e582c44f1831b0cec90d1f672ac180c98 Mon Sep 17 00:00:00 2001 From: isabelizimm Date: Mon, 19 Sep 2022 11:26:01 -0400 Subject: [PATCH 1/2] add examples to docstrings --- docs/source/index.rst | 5 +-- vetiver/pin_read_write.py | 10 ++++++ vetiver/rsconnect.py | 22 ++++++++++++- vetiver/server.py | 51 ++++++++++++++++++++++++------ vetiver/tests/test_add_endpoint.py | 4 +-- vetiver/vetiver_model.py | 10 +++++- vetiver/write_docker.py | 20 ++++++++++-- vetiver/write_fastapi.py | 19 +++++++++-- 8 files changed, 121 insertions(+), 20 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 0bb7bd56..e721ff83 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -70,11 +70,12 @@ Monitor Model Handlers ================== + .. autosummary:: :toctree: reference/ - :caption: Monitor + :caption: Model Handlers - ~BaseHandler + ~handlers.BaseHandler ~SKLearnHandler ~TorchHandler ~StatsmodelsHandler diff --git a/vetiver/pin_read_write.py b/vetiver/pin_read_write.py index 4f845563..375c1055 100644 --- a/vetiver/pin_read_write.py +++ b/vetiver/pin_read_write.py @@ -30,6 +30,16 @@ def vetiver_pin_write(board, model: VetiverModel, versioned: bool = True): VetiverModel to be written to board versioned: bool Whether or not the pin should be versioned + + Example + ------- + >>> import vetiver + >>> from pins import board_temp + >>> model_board = board_temp(versioned = True, allow_pickle_read = True) + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, model_name = "my_model", ptype_data = X) + >>> vetiver.vetiver_pin_write(model_board, v) """ if not board.allow_pickle_read: raise NotImplementedError # must be pickle-able diff --git a/vetiver/rsconnect.py b/vetiver/rsconnect.py index 2da218f5..1152b440 100644 --- a/vetiver/rsconnect.py +++ b/vetiver/rsconnect.py @@ -26,7 +26,7 @@ def deploy_rsconnect( Parameters ---------- - connect_server: + connect_server: rsconnect.api.RSConnectServer RSConnect Server board: Pins board @@ -52,6 +52,26 @@ def deploy_rsconnect( Callback to use to write the log to image: str Docker image to be specified for off-host execution + + Example + ------- + >>> import vetiver + >>> import pins + >>> import rsconnect + >>> board = pins.board_temp(allow_pickle_read=True) + >>> connect_server = rsconnect.api.RSConnectServer( + ... url = url, + ... api_key = api_key) # doctest: +SKIP + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, + ... model_name = "my_model", + ... ptype_data = X) + >>> vetiver.deploy_rsconnect( + ... connect_server = connect_server, + ... board = board, + ... pin_name = "my_model" + ... ) # doctest: +SKIP """ if not title: title = pin_name + "_vetiver" diff --git a/vetiver/server.py b/vetiver/server.py index 98bb182c..6ef50043 100644 --- a/vetiver/server.py +++ b/vetiver/server.py @@ -15,7 +15,7 @@ class VetiverAPI: """Create model aware API - Attributes + Parameters ---------- model : VetiverModel Model to be deployed in API @@ -23,8 +23,14 @@ class VetiverAPI: Determine if data prototype should be enforced app_factory : Type of API to be deployed - app : - API that is deployed + + Example + ------- + >>> import vetiver + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, model_name = "my_model", ptype_data = X) + >>> v_api = vetiver.VetiverAPI(model = v, check_ptype = True) """ app = None @@ -133,7 +139,7 @@ async def rapidoc(): def vetiver_post( self, endpoint_fx: Callable, endpoint_name: str = "custom_endpoint" ): - """Create new POST endpoint + """Create new POST endpoint that is aware of model input data Parameters ---------- @@ -142,17 +148,23 @@ def vetiver_post( endpoint_name : str Name of endpoint - Returns + Example ------- - dict - Key: endpoint_name Value: Output of endpoint_fx, in list format + >>> import vetiver + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, model_name = "model", ptype_data = X) + >>> v_api = vetiver.VetiverAPI(model = v, check_ptype = True) + >>> def sum_values(x): + ... return x.sum() + >>> v_api.vetiver_post(sum_values, "sums") """ if self.check_ptype is True: @self.app.post("/" + endpoint_name) async def custom_endpoint(input_data: self.model.ptype): y = _prepare_data(input_data) - new = endpoint_fx(pd.Series(y)) + new = endpoint_fx(pd.DataFrame(y)) return {endpoint_name: new.tolist()} else: @@ -160,7 +172,7 @@ async def custom_endpoint(input_data: self.model.ptype): @self.app.post("/" + endpoint_name) async def custom_endpoint(input_data: Request): y = await input_data.json() - new = endpoint_fx(pd.Series(y)) + new = endpoint_fx(pd.DataFrame(y)) return {endpoint_name: new.tolist()} @@ -174,6 +186,15 @@ def run(self, port: int = 8000, host: str = "127.0.0.1", **kw): An integer that indicates the server port that should be listened on. host : str A valid IPv4 or IPv6 address, which the application will listen on. + + Example + ------- + >>> import vetiver + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, model_name = "model", ptype_data = X) + >>> v_api = vetiver.VetiverAPI(model = v, check_ptype = True) + >>> v_api.run() # doctest: +SKIP """ _jupyter_nb() uvicorn.run(self.app, port=port, host=host, **kw) @@ -209,6 +230,13 @@ def predict(endpoint, data: Union[dict, pd.DataFrame, pd.Series], **kw): ------- dict Endpoint_name and list of endpoint_fx output + + Example + ------- + >>> import vetiver + >>> X, y = vetiver.get_mock_data() + >>> endpoint = vetiver.vetiver_endpoint(url='http://127.0.0.1:8000/predict') + >>> vetiver.predict(endpoint, X) # doctest: +SKIP """ if isinstance(endpoint, testclient.TestClient): requester = endpoint @@ -273,5 +301,10 @@ def vetiver_endpoint(url="http://127.0.0.1:8000/predict"): ------- url : str URI path to endpoint + + Example + ------- + >>> import vetiver + >>> endpoint = vetiver.vetiver_endpoint(url='http://127.0.0.1:8000/predict') """ return url diff --git a/vetiver/tests/test_add_endpoint.py b/vetiver/tests/test_add_endpoint.py index a53bde61..67bb7296 100644 --- a/vetiver/tests/test_add_endpoint.py +++ b/vetiver/tests/test_add_endpoint.py @@ -30,7 +30,7 @@ def test_endpoint_adds_ptype(): data = {"B": 0, "C": 0, "D": 0} response = client.post("/sum", json=data) assert response.status_code == 200, response.text - assert response.json() == {"sum": 0}, response.json() + assert response.json() == {"sum": [0]}, response.json() def test_endpoint_adds_no_ptype(): @@ -40,4 +40,4 @@ def test_endpoint_adds_no_ptype(): data = [0, 0, 0] response = client.post("/sum", json=data) assert response.status_code == 200, response.text - assert response.json() == {"sum": 0}, response.json() + assert response.json() == {"sum": [0]}, response.json() diff --git a/vetiver/vetiver_model.py b/vetiver/vetiver_model.py index 1333bf7a..237036ab 100644 --- a/vetiver/vetiver_model.py +++ b/vetiver/vetiver_model.py @@ -25,7 +25,7 @@ class VetiverModel: ---------- model : A trained model, such as an sklearn or torch model - name : string + model_name : string Model name or ID ptype_data : pd.DataFrame, np.array Sample of data model should expect when it is being served @@ -49,6 +49,14 @@ class VetiverModel: VetiverModel can also take an initialized custom VetiverHandler as a model, for advanced use cases or non-supported model types. + Example + ------- + >>> from vetiver import mock, VetiverModel + >>> X, y = mock.get_mock_data() + >>> model = mock.get_mock_model().fit(X, y) + >>> v = VetiverModel(model = model, model_name = "my_model", ptype_data = X) + >>> v.description + "Scikit-learn model" """ def __init__( diff --git a/vetiver/write_docker.py b/vetiver/write_docker.py index 3a63d56d..5d328a57 100644 --- a/vetiver/write_docker.py +++ b/vetiver/write_docker.py @@ -29,8 +29,8 @@ def write_docker( ): """Writes a Dockerfile to run VetiverAPI in a container - Args - ---- + Parameters + ---------- app_file: str File containing VetiverAPI to be deployed into container path: str @@ -41,6 +41,22 @@ def write_docker( Host address to run VetiverAPI from Dockerfile port: str Port to run VetiverAPI from Dockerfile + + Example + ------- + >>> import vetiver + >>> import tempfile + >>> import pins + >>> tmp = tempfile.TemporaryDirectory() + >>> board = pins.board_temp(allow_pickle_read=True) + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, model_name = "my_model", ptype_data = X) + >>> vetiver.vetiver_pin_write(board, v) + >>> vetiver.write_app(board, + ... "my_model", + ... file = tmp.name + "/app.py") # need file for model + >>> vetiver.write_docker(app_file = "app.py", path = tmp.name) """ py_version = str(sys.version_info.major) + "." + str(sys.version_info.minor) diff --git a/vetiver/write_fastapi.py b/vetiver/write_fastapi.py index 30cd47d9..66bc774d 100644 --- a/vetiver/write_fastapi.py +++ b/vetiver/write_fastapi.py @@ -56,9 +56,9 @@ def write_app( ): """Write VetiverAPI app to a file - Args - ---- - board : pinse.BaseBoard + Parameters + ---------- + board : API to be written pin_name : string Name of pin containing VetiverModel @@ -66,6 +66,19 @@ def write_app( Pins version of VetiverModel file : Name of file + + Example + ------- + >>> import vetiver + >>> import pins + >>> import tempfile + >>> tmp = tempfile.TemporaryFile() + >>> board = pins.board_temp(allow_pickle_read=True) + >>> X, y = vetiver.get_mock_data() + >>> model = vetiver.get_mock_model().fit(X, y) + >>> v = vetiver.VetiverModel(model = model, model_name = "my_model", ptype_data = X) + >>> vetiver.vetiver_pin_write(board, v) + >>> vetiver.write_app(board, "my_model", file = tmp.name) """ if board.versioned: From a34fcb4cc42262fdcefdfaf0a1cd631771ad9c93 Mon Sep 17 00:00:00 2001 From: isabelizimm Date: Mon, 19 Sep 2022 11:46:57 -0400 Subject: [PATCH 2/2] update tmpfile for windows --- vetiver/write_fastapi.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/vetiver/write_fastapi.py b/vetiver/write_fastapi.py index 66bc774d..f6a8dd68 100644 --- a/vetiver/write_fastapi.py +++ b/vetiver/write_fastapi.py @@ -70,15 +70,17 @@ def write_app( Example ------- >>> import vetiver - >>> import pins >>> import tempfile - >>> tmp = tempfile.TemporaryFile() + >>> import pins + >>> tmp = tempfile.TemporaryDirectory() >>> board = pins.board_temp(allow_pickle_read=True) >>> X, y = vetiver.get_mock_data() >>> model = vetiver.get_mock_model().fit(X, y) >>> v = vetiver.VetiverModel(model = model, model_name = "my_model", ptype_data = X) >>> vetiver.vetiver_pin_write(board, v) - >>> vetiver.write_app(board, "my_model", file = tmp.name) + >>> vetiver.write_app(board, + ... "my_model", + ... file = tmp.name + "/app.py") """ if board.versioned: