Passed
Push — master ( f6c774...627e72 )
by
unknown
15:38
created

tabpy.tabpy_tools.rest_client   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Test Coverage

Coverage 80.52%

Importance

Changes 0
Metric Value
wmc 22
eloc 92
dl 0
loc 255
ccs 62
cts 77
cp 0.8052
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 12 1
A RESTServiceClient.set_endpoint() 0 11 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
    methods = RESTProperty(list)
45 1
    creation_time = RESTProperty(datetime, from_epoch, to_epoch)
46 1
    last_modified_time = RESTProperty(datetime, from_epoch, to_epoch)
47 1
    evaluator = RESTProperty(str)
48 1
    schema_version = RESTProperty(int)
49 1
    schema = RESTProperty(str)
50 1
    isPublic = RESTProperty(bool)
51
52 1
    def __new__(cls, **kwargs):
53
        """Dispatch to the appropriate class."""
54 1
        cls2 = {"alias": AliasEndpoint, "model": ModelEndpoint}[kwargs["type"]]
55
56 1
        """return object.__new__(cls, **kwargs)"""
57 1
        """ modified for Python 3"""
58 1
        return object.__new__(cls2)
59
60 1
    def __eq__(self, other):
61
        return (
62
            self.name == other.name
63
            and self.type == other.type
64
            and self.version == other.version
65
            and self.description == other.description
66
            and self.dependencies == other.dependencies
67
            and self.methods == other.methods
68
            and self.evaluator == other.evaluator
69
            and self.schema_version == other.schema_version
70
            and self.schema == other.schema
71
            and self.isPublic == other.isPublic
72
        )
73
74
75 1
class ModelEndpoint(Endpoint):
76
    """Represents a model endpoint.
77
78
    src_path : str
79
80
        The local file path to the source of this object.
81
82
    required_files : str
83
84
        The local file path to the directory containing the
85
        required files.
86
87
    required_packages : str
88
89
        The local file path to the directory containing the
90
        required packages.
91
92
    """
93
94 1
    src_path = RESTProperty(str)
95 1
    required_files = RESTProperty(list)
96 1
    required_packages = RESTProperty(list)
97 1
    required_packages_dst_path = RESTProperty(str)
98
99 1
    def __init__(self, **kwargs):
100 1
        super().__init__(**kwargs)
101 1
        self.type = "model"
102
103 1
    def __eq__(self, other):
104
        return (
105
            super().__eq__(other)
106
            and self.required_files == other.required_files
107
            and self.required_packages == other.required_packages
108
        )
109
110
111 1
class AliasEndpoint(Endpoint):
112
    """Represents an alias Endpoint.
113
114
    target : str
115
116
        The endpoint that this is an alias for.
117
118
    """
119
120 1
    target = RESTProperty(str)
121
122 1
    def __init__(self, **kwargs):
123
        super().__init__(**kwargs)
124
        self.type = "alias"
125
126
127 1
class RESTServiceClient:
128
    """A thin client for the REST Service."""
129
130 1
    def __init__(self, service_client):
131 1
        self.service_client = service_client
132 1
        self.query_timeout = None
133
134 1
    def get_info(self):
135
        """Returns the /info"""
136
        return self.service_client.GET("info")
137
138 1
    def query(self, name, *args, **kwargs):
139
        """Performs a query. Either specify *args or **kwargs, not both.
140
        Respects query_timeout."""
141
        if args and kwargs:
142
            raise ValueError(
143
                "Mixing of keyword arguments and positional arguments when "
144
                "querying an endpoint is not supported."
145
            )
146
        return self.service_client.POST(
147
            "query/" + name, data={"data": args or kwargs}, timeout=self.query_timeout
148
        )
149
150 1
    def get_endpoint_upload_destination(self):
151
        """Returns a dict representing where endpoint data should be uploaded.
152
153
        Returns
154
        -------
155
        dict
156
            Keys include:
157
            * path: a local file path.
158
159
        Note: In the future, other paths and parameters may be supported.
160
161
        Note: At this time, the response should not change over time.
162
        """
163 1
        return self.service_client.GET("configurations/endpoint_upload_destination")
164
165 1
    def get_endpoints(self, type=None):
166
        """Returns endpoints from the management API.
167
168
        Parameters
169
        ----------
170
171
        type : str
172
            The type of endpoint to return. None will include all endpoints.
173
            Other options are 'model' and 'alias'.
174
        """
175 1
        result = {}
176 1
        for name, attrs in self.service_client.GET("endpoints", {"type": type}).items():
177 1
            endpoint = Endpoint.from_json(attrs)
178 1
            endpoint.name = name
179 1
            result[name] = endpoint
180 1
        return result
181
182 1
    def get_endpoint(self, endpoint_name):
183
        """Returns an endpoints from the management API given its name.
184
185
        Parameters
186
        ----------
187
188
        endpoint_name : str
189
190
            The name of the endpoint.
191
        """
192
        ((name, attrs),) = self.service_client.GET("endpoints/" + endpoint_name).items()
193
        endpoint = Endpoint.from_json(attrs)
194
        endpoint.name = name
195
        return endpoint
196
197 1
    def add_endpoint(self, endpoint):
198
        """Adds an endpoint through the management API.
199
200
        Parameters
201
        ----------
202
203
        endpoint : Endpoint
204
        """
205 1
        return self.service_client.POST("endpoints", endpoint.to_json())
206
207 1
    def set_endpoint(self, endpoint):
208
        """Updates an endpoint through the management API.
209
210
        Parameters
211
        ----------
212
213
        endpoint : Endpoint
214
215
            The endpoint to update.
216
        """
217 1
        return self.service_client.PUT("endpoints/" + endpoint.name, endpoint.to_json())
218
219 1
    def remove_endpoint(self, endpoint_name):
220
        """Deletes an endpoint through the management API.
221
222
        Parameters
223
        ----------
224
225
        endpoint_name : str
226
227
            The endpoint to delete.
228
        """
229
        self.service_client.DELETE("endpoints/" + endpoint_name)
230
231 1
    def get_status(self):
232
        """Returns the status of the server.
233
234
        Returns
235
        -------
236
237
        dict
238
        """
239 1
        return self.service_client.GET("status")
240
241 1
    def set_credentials(self, username, password):
242
        """
243
        Set credentials for all the TabPy client-server communication
244
        where client is tabpy-tools and server is tabpy-server.
245
246
        Parameters
247
        ----------
248
        username : str
249
            User name (login). Username is case insensitive.
250
251
        password : str
252
            Password in plain text.
253
        """
254
        self.service_client.set_credentials(username, password)
255