Passed
Push — master ( 59f405...fe613a )
by
unknown
16:40 queued 15s
created

tabpy.tabpy_tools.rest_client   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 265
Duplicated Lines 0 %

Test Coverage

Coverage 81.25%

Importance

Changes 0
Metric Value
wmc 22
eloc 96
dl 0
loc 265
ccs 65
cts 80
cp 0.8125
rs 10
c 0
b 0
f 0

2 Functions

Rating   Name   Duplication   Size   Complexity  
A to_epoch() 0 2 1
A from_epoch() 0 5 2

16 Methods

Rating   Name   Duplication   Size   Complexity  
A RESTServiceClient.get_status() 0 9 1
A RESTServiceClient.remove_endpoint() 0 11 1
A RESTServiceClient.add_endpoint() 0 9 1
A ModelEndpoint.__eq__() 0 5 1
A RESTServiceClient.get_endpoints() 0 16 2
A RESTServiceClient.get_endpoint_upload_destination() 0 14 1
A RESTServiceClient.get_info() 0 3 1
A RESTServiceClient.get_endpoint() 0 14 1
A AliasEndpoint.__init__() 0 3 1
A Endpoint.__new__() 0 7 1
A Endpoint.__eq__() 0 13 1
A RESTServiceClient.set_endpoint() 0 19 1
A RESTServiceClient.__init__() 0 3 1
A ModelEndpoint.__init__() 0 3 1
A RESTServiceClient.set_credentials() 0 14 1
A RESTServiceClient.query() 0 10 3
1 1
from .rest import RESTObject, RESTProperty
2 1
from datetime import datetime
3
4
5 1
def from_epoch(value):
6 1
    if isinstance(value, datetime):
7
        return value
8
    else:
9 1
        return datetime.utcfromtimestamp(value)
10
11
12 1
def to_epoch(value):
13
    return (value - datetime(1970, 1, 1)).total_seconds()
14
15
16 1
class Endpoint(RESTObject):
17
    """Represents an endpoint.
18
19
    Note that not every attribute is returned as part of the GET.
20
21
    Attributes
22
    ----------
23
24
    name : str
25
        The name of the endpoint. Valid names include ``[a-zA-Z0-9_\\- ]+``
26
    type : str
27
        The type of endpoint. The types include "alias", "model".
28
    version : int
29
        The version of this endpoint. Initial versions have version on 1. New
30
        versions increment this by 1.
31
    description : str
32
        A human-readable description of the endpoint.
33
    dependencies: list
34
        A list of endpoints that this endpoint depends on.
35
    methods : list
36
        ???
37
    """
38
39 1
    name = RESTProperty(str)
40 1
    type = RESTProperty(str)
41 1
    version = RESTProperty(int)
42 1
    description = RESTProperty(str)
43 1
    dependencies = RESTProperty(list)
44 1
    docstring = RESTProperty(str)
45 1
    methods = RESTProperty(list)
46 1
    creation_time = RESTProperty(datetime, from_epoch, to_epoch)
47 1
    last_modified_time = RESTProperty(datetime, from_epoch, to_epoch)
48 1
    evaluator = RESTProperty(str)
49 1
    schema_version = RESTProperty(int)
50 1
    schema = RESTProperty(str)
51 1
    is_public = RESTProperty(bool)
52
53 1
    def __new__(cls, **kwargs):
54
        """Dispatch to the appropriate class."""
55 1
        cls2 = {"alias": AliasEndpoint, "model": ModelEndpoint}[kwargs["type"]]
56
57 1
        """return object.__new__(cls, **kwargs)"""
58 1
        """ modified for Python 3"""
59 1
        return object.__new__(cls2)
60
61 1
    def __eq__(self, other):
62
        return (
63
            self.name == other.name
64
            and self.type == other.type
65
            and self.version == other.version
66
            and self.description == other.description
67
            and self.dependencies == other.dependencies
68
            and self.docstring == other.docstring
69
            and self.methods == other.methods
70
            and self.evaluator == other.evaluator
71
            and self.schema_version == other.schema_version
72
            and self.schema == other.schema
73
            and self.is_public == other.is_public
74
        )
75
76
77 1
class ModelEndpoint(Endpoint):
78
    """Represents a model endpoint.
79
80
    src_path : str
81
82
        The local file path to the source of this object.
83
84
    required_files : str
85
86
        The local file path to the directory containing the
87
        required files.
88
89
    required_packages : str
90
91
        The local file path to the directory containing the
92
        required packages.
93
94
    """
95
96 1
    src_path = RESTProperty(str)
97 1
    required_files = RESTProperty(list)
98 1
    required_packages = RESTProperty(list)
99 1
    required_packages_dst_path = RESTProperty(str)
100
101 1
    def __init__(self, **kwargs):
102 1
        super().__init__(**kwargs)
103 1
        self.type = "model"
104
105 1
    def __eq__(self, other):
106
        return (
107
            super().__eq__(other)
108
            and self.required_files == other.required_files
109
            and self.required_packages == other.required_packages
110
        )
111
112
113 1
class AliasEndpoint(Endpoint):
114
    """Represents an alias Endpoint.
115
116
    target : str
117
118
        The endpoint that this is an alias for.
119
120
    """
121
122 1
    target = RESTProperty(str)
123
124 1
    def __init__(self, **kwargs):
125
        super().__init__(**kwargs)
126
        self.type = "alias"
127
128
129 1
class RESTServiceClient:
130
    """A thin client for the REST Service."""
131
132 1
    def __init__(self, service_client):
133 1
        self.service_client = service_client
134 1
        self.query_timeout = None
135
136 1
    def get_info(self):
137
        """Returns the /info"""
138
        return self.service_client.GET("info")
139
140 1
    def query(self, name, *args, **kwargs):
141
        """Performs a query. Either specify *args or **kwargs, not both.
142
        Respects query_timeout."""
143
        if args and kwargs:
144
            raise ValueError(
145
                "Mixing of keyword arguments and positional arguments when "
146
                "querying an endpoint is not supported."
147
            )
148
        return self.service_client.POST(
149
            "query/" + name, data={"data": args or kwargs}, timeout=self.query_timeout
150
        )
151
152 1
    def get_endpoint_upload_destination(self):
153
        """Returns a dict representing where endpoint data should be uploaded.
154
155
        Returns
156
        -------
157
        dict
158
            Keys include:
159
            * path: a local file path.
160
161
        Note: In the future, other paths and parameters may be supported.
162
163
        Note: At this time, the response should not change over time.
164
        """
165 1
        return self.service_client.GET("configurations/endpoint_upload_destination")
166
167 1
    def get_endpoints(self, type=None):
168
        """Returns endpoints from the management API.
169
170
        Parameters
171
        ----------
172
173
        type : str
174
            The type of endpoint to return. None will include all endpoints.
175
            Other options are 'model' and 'alias'.
176
        """
177 1
        result = {}
178 1
        for name, attrs in self.service_client.GET("endpoints", {"type": type}).items():
179 1
            endpoint = Endpoint.from_json(attrs)
180 1
            endpoint.name = name
181 1
            result[name] = endpoint
182 1
        return result
183
184 1
    def get_endpoint(self, endpoint_name):
185
        """Returns an endpoints from the management API given its name.
186
187
        Parameters
188
        ----------
189
190
        endpoint_name : str
191
192
            The name of the endpoint.
193
        """
194
        ((name, attrs),) = self.service_client.GET("endpoints/" + endpoint_name).items()
195
        endpoint = Endpoint.from_json(attrs)
196
        endpoint.name = name
197
        return endpoint
198
199 1
    def add_endpoint(self, endpoint):
200
        """Adds an endpoint through the management API.
201
202
        Parameters
203
        ----------
204
205
        endpoint : Endpoint
206
        """
207 1
        return self.service_client.POST("endpoints", endpoint.to_json())
208
209 1
    def set_endpoint(self, endpoint, should_update_version=True):
210
        """Updates an endpoint through the management API.
211
212
        Parameters
213
        ----------
214
215
        endpoint : Endpoint
216
217
            The endpoint to update.
218
219
        should_update_version : bool
220
221
            Whether this update should increment the version.
222
            False if this is called from update_endpoint_info
223
            True if called from deploy
224
        """
225 1
        request_body = endpoint.to_json()
226 1
        request_body["should_update_version"] = should_update_version
227 1
        return self.service_client.PUT("endpoints/" + endpoint.name, request_body)
228
229 1
    def remove_endpoint(self, endpoint_name):
230
        """Deletes an endpoint through the management API.
231
232
        Parameters
233
        ----------
234
235
        endpoint_name : str
236
237
            The endpoint to delete.
238
        """
239
        self.service_client.DELETE("endpoints/" + endpoint_name)
240
241 1
    def get_status(self):
242
        """Returns the status of the server.
243
244
        Returns
245
        -------
246
247
        dict
248
        """
249 1
        return self.service_client.GET("status")
250
251 1
    def set_credentials(self, username, password):
252
        """
253
        Set credentials for all the TabPy client-server communication
254
        where client is tabpy-tools and server is tabpy-server.
255
256
        Parameters
257
        ----------
258
        username : str
259
            User name (login). Username is case insensitive.
260
261
        password : str
262
            Password in plain text.
263
        """
264
        self.service_client.set_credentials(username, password)
265