Skip to content

Commit f58bf7f

Browse files
authored
Merge pull request #34 from tmeiczin/tjm/access
Tjm/access
2 parents da6355a + 748ec0c commit f58bf7f

File tree

9 files changed

+97
-59
lines changed

9 files changed

+97
-59
lines changed

example/resources/form.py

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66

77
from reliqua.resources.base import Resource
8+
from reliqua.status_codes import HTTP
89

910

1011
class Contact(Resource):
@@ -34,3 +35,6 @@ def on_post(self, req, resp):
3435
p = req.params
3536
if p.get("subject"):
3637
resp.media = {"success": True}
38+
else:
39+
resp.status = HTTP("400")
40+
resp.media = {"error": "Subject is required"}

example/resources/media.py

+28-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88

99
from reliqua.resources.base import Resource
10+
from reliqua.status_codes import HTTP
1011

1112

1213
class Gzip(Resource):
@@ -16,19 +17,25 @@ class Gzip(Resource):
1617
"/gzip": {},
1718
}
1819

19-
def on_get(self, req, resp):
20+
def on_get(self, _req, resp):
2021
"""
2122
Send contact message.
2223
2324
:accepts json: Accepts JSON
24-
:response 200 binary: All good
25-
:return gzip: Return data
25+
:response 200 binary: All good
26+
:return gzip: Return data
2627
"""
27-
fh = open("/tmp/hello.txt.gz", "rb")
28-
resp.append_header("Content-Disposition", "attachment; filename=hello.txt.gz")
29-
resp.content_type = "application/gzip"
30-
resp.content_encoding = "gzip"
31-
resp.data = fh.read()
28+
try:
29+
with open("/tmp/hello.txt.gz", "rb") as fh:
30+
resp.append_header("Content-Disposition", "attachment; filename=hello.txt.gz")
31+
resp.content_type = "application/gzip"
32+
resp.content_encoding = "gzip"
33+
resp.data = fh.read()
34+
except FileNotFoundError:
35+
resp.status = HTTP("404")
36+
except Exception as e:
37+
resp.status = HTTP("500")
38+
resp.media = {"error": str(e)}
3239

3340

3441
class Binary(Resource):
@@ -38,16 +45,23 @@ class Binary(Resource):
3845
"/bin/{filename}": {},
3946
}
4047

41-
def on_get(self, req, resp, filename):
48+
def on_get(self, _req, resp, filename):
4249
"""
4350
Send contact message.
4451
4552
:param str filename: [in=path] Filename
46-
:response 200 binary: All good
53+
:response 200 binary: All good
4754
:return binary: Return data
4855
"""
4956
path = f"/tmp/{filename}"
50-
resp.stream = open(path, "rb")
51-
resp.content_type = "application/gzip"
52-
resp.content_encoding = "gzip"
53-
resp.content_length = os.path.getsize(path)
57+
try:
58+
with open(path, "rb") as fh:
59+
resp.stream = fh
60+
resp.content_type = "application/gzip"
61+
resp.content_encoding = "gzip"
62+
resp.content_length = os.path.getsize(path)
63+
except FileNotFoundError:
64+
resp.status = HTTP("404")
65+
except Exception as e:
66+
resp.status = HTTP("500")
67+
resp.media = {"error": str(e)}

example/resources/servers.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,20 @@ class Server(Resource):
2626

2727
def on_get_by_id(self, _req, resp, id=None):
2828
"""
29-
Retrieve servers.
29+
Retrieve a server.
3030
31-
Retrieve a list of servers in the lab.
31+
Retrieve a server by its ID.
3232
3333
:param str id: [in=path, required] Server ID
3434
35-
:response 200: server was retrieved
36-
:response 400: invalid query parameter
35+
:response 200: Server was retrieved
36+
:response 404: Server not found
3737
3838
:return json:
3939
"""
4040
try:
41-
resp.media = servers[id]
42-
except IndexError:
41+
resp.media = servers[int(id)]
42+
except (IndexError, ValueError):
4343
resp.status = HTTP("404")
4444

4545

@@ -54,14 +54,14 @@ class Servers(Resource):
5454

5555
def on_get(self, req, resp):
5656
"""
57-
Retrieve a server.
57+
Retrieve servers.
5858
59-
Retrieve server information
59+
Retrieve a list of servers in the lab.
6060
6161
:param list labs: [in=query] The labs servers are located
6262
63-
:response 200: server was retrieved
64-
:response 400: invalid query parameter
63+
:response 200: Servers were retrieved
64+
:response 400: Invalid query parameter
6565
6666
:return json:
6767
"""
@@ -70,18 +70,18 @@ def on_get(self, req, resp):
7070

7171
def on_get_by_cpu(self, _req, resp, cpus=1):
7272
"""
73-
Retrieve a server by cpu.
73+
Retrieve a server by CPU.
7474
75-
Retrieve server information by cpu
75+
Retrieve server information by CPU.
7676
7777
:param int cpus: [in=path required] Number of CPUs for server
7878
79-
:response 200: server was retrieved
80-
:response 400: invalid query parameter
79+
:response 200: Server was retrieved
80+
:response 404: Server not found
8181
8282
:return json:
8383
"""
8484
try:
85-
resp.media = servers[cpus]
86-
except IndexError:
85+
resp.media = servers[int(cpus)]
86+
except (IndexError, ValueError):
8787
resp.status = HTTP("404")

example/resources/users.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def on_get_by_id(self, _req, resp, id=None):
6464
"""
6565
try:
6666
resp.media = users[int(id)]
67-
except IndexError:
67+
except (IndexError, ValueError):
6868
resp.status = HTTP("404")
6969

7070
def on_delete_by_id(self, _req, resp, id=None):
@@ -78,7 +78,7 @@ def on_delete_by_id(self, _req, resp, id=None):
7878
try:
7979
users.pop(int(id))
8080
resp.media = {"success": True}
81-
except IndexError:
81+
except (IndexError, ValueError):
8282
resp.status = HTTP("400")
8383

8484

src/reliqua/api.py

+16-3
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def __init__(
6565
:param dict info: Application information
6666
:param dict openapi: OpenAPI configuration options
6767
68-
:return: api instance
68+
:return: API instance
6969
"""
7070
info = info or {}
7171
openapi = openapi or {}
@@ -107,6 +107,7 @@ def __init__(
107107
self._add_docs()
108108

109109
def _add_handlers(self):
110+
"""Add custom media handlers."""
110111
extra_handlers = {
111112
"application/yaml": YAMLHandler(),
112113
"text/html; charset=utf-8": TextHandler(),
@@ -118,6 +119,7 @@ def _add_handlers(self):
118119
self.resp_options.media_handlers.update(extra_handlers)
119120

120121
def _load_resources(self):
122+
"""Load resource classes from the specified resource path."""
121123
resources = []
122124
path = f"{self.resource_path}/*.py"
123125
print(f"searching {path}")
@@ -128,10 +130,11 @@ def _load_resources(self):
128130
classes = self._get_classes(file)
129131
resources.extend(classes)
130132

131-
# add config to resource
133+
# Add config to resource
132134
self.resources = [x(app_config=self.config, **self.resource_attributes) for x in resources]
133135

134136
def _is_route_method(self, name, suffix):
137+
"""Check if a method name is a route method."""
135138
if not name.startswith("on_"):
136139
return None
137140

@@ -141,6 +144,7 @@ def _is_route_method(self, name, suffix):
141144
return re.search(r"^on_([a-z]+)$", name)
142145

143146
def _parse_methods(self, resource, route, methods):
147+
"""Parse methods of a resource for a given route."""
144148
parser = SphinxParser()
145149
for name in methods:
146150
operation_id = f"{resource.__class__.__name__}.{name}"
@@ -149,23 +153,30 @@ def _parse_methods(self, resource, route, methods):
149153
resource.__data__[route][action] = parser.parse(method, operation_id=operation_id)
150154

151155
def _parse_resource(self, resource):
156+
"""Parse a resource to extract routes and methods."""
152157
for route, data in resource.__routes__.items():
153158
resource.__data__[route] = {}
154159
suffix = data.get("suffix", None)
155160
methods = [x for x in dir(resource) if self._is_route_method(x, suffix)]
156161
self._parse_methods(resource, route, methods)
157162

158163
def _parse_docstrings(self):
164+
"""Parse docstrings of all resources."""
159165
for resource in self.resources:
160166
resource.__data__ = {}
161167
self._parse_resource(resource)
162168

163169
def _get_classes(self, filename):
170+
"""Get resource classes from a file."""
164171
classes = []
165172
module_name = str(uuid.uuid3(uuid.NAMESPACE_OID, filename))
166173
spec = importlib.util.spec_from_file_location(module_name, filename)
167174
module = importlib.util.module_from_spec(spec)
168-
spec.loader.exec_module(module)
175+
try:
176+
spec.loader.exec_module(module)
177+
except (ImportError, FileNotFoundError, SyntaxError, TypeError, AttributeError) as e:
178+
print(f"Error loading module {module_name}: {e}")
179+
return classes
169180

170181
for _, c in inspect.getmembers(module, inspect.isclass):
171182
if issubclass(c, Resource) and hasattr(c, "__routes__"):
@@ -174,12 +185,14 @@ def _get_classes(self, filename):
174185
return classes
175186

176187
def _add_routes(self):
188+
"""Add routes for all resources."""
177189
for resource in self.resources:
178190
routes = resource.__routes__
179191
for route, kwargs in routes.items():
180192
self.add_route(route, resource, **kwargs)
181193

182194
def _add_docs(self):
195+
"""Add Swagger and OpenAPI documentation routes."""
183196
swagger = Swagger(
184197
self.openapi["static_url"],
185198
self.openapi["spec_url"],

src/reliqua/app.py

+12-10
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,19 @@
4343

4444
def update_dict(a, b):
4545
"""
46-
Update dictionary A with values from dictionary B.
46+
Update dictionary a with values from dictionary b.
4747
48-
Update the diction A with values from dictionary B. Remove
49-
keys from A that are not in B.
48+
Update dictionary a with values from dictionary b. Remove
49+
keys from a that are not in b.
5050
51-
:param dict a: Dictionary A
52-
:param dict b: Dictionary B
51+
:param dict a: Dictionary to update
52+
:param dict b: Dictionary with new values
53+
:return dict: Updated dictionary
5354
"""
54-
# Create a new dictionary with keys from A that are also in B
55+
# Create a new dictionary with keys from a that are also in b
5556
updated_dict = {key: a[key] for key in a if key in b}
5657

57-
# Add keys from B that are not in A
58+
# Add keys from b that are not in a
5859
for key in b:
5960
if key not in updated_dict:
6061
updated_dict[key] = b[key]
@@ -78,8 +79,9 @@ def load_config(config_file):
7879

7980
for option in config.options(section):
8081
params[option] = config.get(section, option)
81-
except TypeError:
82-
pass
82+
except (TypeError, configparser.Error) as e:
83+
# Log the error or handle it appropriately
84+
print(f"Error loading config file: {e}")
8385

8486
return params
8587

@@ -125,7 +127,7 @@ def __init__(
125127

126128
middleware.append(ProcessParams())
127129

128-
# trim slashes from proxy URL if specified; otherwise set default proxy url
130+
# Trim slashes from proxy URL if specified; otherwise set default proxy URL
129131
bind = self.gunicorn_options["bind"]
130132
openapi["api_url"] = openapi["api_url"].rstrip("/") if openapi["api_url"] else f"http://{bind}"
131133

src/reliqua/docs.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,16 @@ def __init__(self, schema):
5858
:param dict schema: Documents JSON schema
5959
:return: None
6060
"""
61+
super().__init__()
6162
self.schema = schema
6263

6364
def on_get(self, _req, resp):
6465
"""
6566
Return the JSON document schema.
6667
67-
:param Response response: Response object
68-
:return: None
68+
:param Request _req: Request object
69+
:param Response resp: Response object
70+
:return: None
6971
"""
7072
resp.set_header("Access-Control-Allow-Origin", "*")
7173
resp.media = self.schema

src/reliqua/openapi.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def __init__(
115115
self.examples = examples
116116
self._datatype = datatype
117117

118-
# path parameters are always required
118+
# Path parameters are always required
119119
if self.location == "path":
120120
self.required = True
121121

@@ -125,7 +125,7 @@ def datatype(self):
125125
value = self._datatype
126126
a, _, b = value.partition("|")
127127

128-
# handle defined list type ex: list[str]
128+
# Handle defined list type ex: list[str]
129129
if re.search(r"list\[(\w+)]", a):
130130
a = "list"
131131

0 commit comments

Comments
 (0)