Skip to content

Commit 5bac488

Browse files
authored
Fixed mypy code smells and added mypy to the CI/CD pipeline (#143)
* fix basic mypy command errors * fix errors for base defs * add new support tests * added ignore statements * fix flake8 errors * add workflow command * fix mypy imports * fix mypy errors
1 parent 05be4df commit 5bac488

File tree

14 files changed

+156
-88
lines changed

14 files changed

+156
-88
lines changed

.github/workflows/checks.yml

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
pip install -r requirements_dev.txt
4040
- name: Lint with flake8
4141
run: flake8
42+
- name: Run mypy
43+
run: mypy foca
4244
- name: Start MongoDB
4345
uses: supercharge/[email protected]
4446
with:

foca/access_control/access_control_server.py

+72-52
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from werkzeug.exceptions import (InternalServerError, NotFound)
1010

1111
from foca.utils.logging import log_traffic
12+
from foca.errors.exceptions import BadRequest
1213

1314
logger = logging.getLogger(__name__)
1415

@@ -20,27 +21,31 @@ def postPermission() -> str:
2021
Returns:
2122
Identifier of the new permission added.
2223
"""
23-
try:
24-
access_control_adapter = current_app.config["casbin_adapter"]
25-
request_json = request.json
26-
rule = request_json.get("rule", {})
27-
permission_data = [
28-
rule.get("v0", None),
29-
rule.get("v1", None),
30-
rule.get("v2", None),
31-
rule.get("v3", None),
32-
rule.get("v4", None),
33-
rule.get("v5", None)
34-
]
35-
permission_id = access_control_adapter.save_policy_line(
36-
ptype=request_json.get("policy_type", None),
37-
rule=permission_data
38-
)
39-
logger.info("New policy added.")
40-
return permission_id
41-
except Exception as e:
42-
logger.error(f"{type(e).__name__}: {e}")
43-
raise InternalServerError
24+
request_json = request.json
25+
if isinstance(request_json, dict):
26+
try:
27+
access_control_adapter = current_app.config["casbin_adapter"]
28+
rule = request_json.get("rule", {})
29+
permission_data = [
30+
rule.get("v0", None),
31+
rule.get("v1", None),
32+
rule.get("v2", None),
33+
rule.get("v3", None),
34+
rule.get("v4", None),
35+
rule.get("v5", None)
36+
]
37+
permission_id = access_control_adapter.save_policy_line(
38+
ptype=request_json.get("policy_type", None),
39+
rule=permission_data
40+
)
41+
logger.info("New policy added.")
42+
return permission_id
43+
except Exception as e:
44+
logger.error(f"{type(e).__name__}: {e}")
45+
raise InternalServerError
46+
else:
47+
logger.error("Invalid request payload.")
48+
raise BadRequest
4449

4550

4651
@log_traffic
@@ -55,27 +60,34 @@ def putPermission(
5560
Returns:
5661
Identifier of updated permission.
5762
"""
58-
try:
59-
request_json = request.json
60-
access_control_config = current_app.config.foca.access_control
61-
db_coll_permission: Collection = (
62-
current_app.config.foca.db.dbs[access_control_config.db_name]
63-
.collections[access_control_config.collection_name].client
64-
)
65-
66-
permission_data = request_json.get("rule", {})
67-
permission_data["id"] = id
68-
permission_data["ptype"] = request_json.get("policy_type", None)
69-
db_coll_permission.replace_one(
70-
filter={"id": id},
71-
replacement=permission_data,
72-
upsert=True
73-
)
74-
logger.info("Policy updated.")
75-
return id
76-
except Exception as e:
77-
logger.error(f"{type(e).__name__}: {e}")
78-
raise InternalServerError
63+
request_json = request.json
64+
if isinstance(request_json, dict):
65+
app_config = current_app.config
66+
try:
67+
access_control_config = \
68+
app_config.foca.access_control # type: ignore[attr-defined]
69+
db_coll_permission: Collection = (
70+
app_config.foca.db.dbs[ # type: ignore[attr-defined]
71+
access_control_config.db_name]
72+
.collections[access_control_config.collection_name].client
73+
)
74+
75+
permission_data = request_json.get("rule", {})
76+
permission_data["id"] = id
77+
permission_data["ptype"] = request_json.get("policy_type", None)
78+
db_coll_permission.replace_one(
79+
filter={"id": id},
80+
replacement=permission_data,
81+
upsert=True
82+
)
83+
logger.info("Policy updated.")
84+
return id
85+
except Exception as e:
86+
logger.error(f"{type(e).__name__}: {e}")
87+
raise InternalServerError
88+
else:
89+
logger.error("Invalid request payload.")
90+
raise BadRequest
7991

8092

8193
@log_traffic
@@ -88,11 +100,13 @@ def getAllPermissions(limit=None) -> List[Dict]:
88100
Returns:
89101
List of permission dicts.
90102
"""
91-
logger.info(f"test {current_app.config}")
92-
access_control_config = current_app.config.foca.access_control
103+
app_config = current_app.config
104+
access_control_config = \
105+
app_config.foca.access_control # type: ignore[attr-defined]
93106
db_coll_permission: Collection = (
94-
current_app.config.foca.db.dbs[access_control_config.db_name]
95-
.collections[access_control_config.collection_name].client
107+
app_config.foca.db.dbs[ # type: ignore[attr-defined]
108+
access_control_config.db_name
109+
].collections[access_control_config.collection_name].client
96110
)
97111

98112
if not limit:
@@ -129,10 +143,13 @@ def getPermission(
129143
Returns:
130144
Permission data for the given id.
131145
"""
132-
access_control_config = current_app.config.foca.access_control
146+
app_config = current_app.config
147+
access_control_config = \
148+
app_config.foca.access_control # type: ignore[attr-defined]
133149
db_coll_permission: Collection = (
134-
current_app.config.foca.db.dbs[access_control_config.db_name]
135-
.collections[access_control_config.collection_name].client
150+
app_config.foca.db.dbs[ # type: ignore[attr-defined]
151+
access_control_config.db_name
152+
].collections[access_control_config.collection_name].client
136153
)
137154

138155
permission = db_coll_permission.find_one(filter={"id": id})
@@ -162,10 +179,13 @@ def deletePermission(
162179
Returns:
163180
Delete permission identifier.
164181
"""
165-
access_control_config = current_app.config.foca.access_control
182+
app_config = current_app.config
183+
access_control_config = \
184+
app_config.foca.access_control # type: ignore[attr-defined]
166185
db_coll_permission: Collection = (
167-
current_app.config.foca.db.dbs[access_control_config.db_name]
168-
.collections[access_control_config.collection_name].client
186+
app_config.foca.db.dbs[ # type: ignore[attr-defined]
187+
access_control_config.db_name
188+
].collections[access_control_config.collection_name].client
169189
)
170190

171191
del_obj_permission = db_coll_permission.delete_one({'id': id})

foca/access_control/foca_casbin_adapter/adapter.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ def save_policy_line(self, ptype: str, rule: List[str]):
6262
line = CasbinRule(ptype=ptype)
6363
for index, value in enumerate(rule):
6464
setattr(line, f"v{index}", value)
65-
line = line.dict()
66-
line["id"] = generate_id()
67-
self._collection.insert_one(line)
68-
return line["id"]
65+
rule_dict: dict = line.dict()
66+
rule_dict["id"] = generate_id()
67+
self._collection.insert_one(rule_dict)
68+
return rule_dict["id"]
6969

7070
def _delete_policy_lines(self, ptype: str, rule: List[str]) -> int:
7171
"""Method to find a delete policies given a list of policy attributes.
@@ -118,27 +118,27 @@ def save_policy(self, model: Model) -> bool:
118118
self.save_policy_line(ptype, rule)
119119
return True
120120

121-
def add_policy(self, sec: str, ptype: str, rule: CasbinRule) -> bool:
121+
def add_policy(self, sec: str, ptype: str, rule: List[str]) -> bool:
122122
"""Add policy rules to mongodb
123123
124124
Args:
125125
sec: Section corresponding which the rule will be added.
126126
ptype: Policy type for which casbin rule will be added.
127-
rule: Casbin rule to be added.
127+
rule: Casbin rule list definition to be added.
128128
129129
Returns:
130130
True if succeed else False.
131131
"""
132132
self.save_policy_line(ptype, rule)
133133
return True
134134

135-
def remove_policy(self, sec: str, ptype: str, rule: CasbinRule):
135+
def remove_policy(self, sec: str, ptype: str, rule: List[str]):
136136
"""Remove policy rules from mongodb(duplicate rules are also removed).
137137
138138
Args:
139139
sec: Section corresponding which the rule will be added.
140140
ptype: Policy type for which casbin rule will be removed.
141-
rule: Casbin rule to be removed.
141+
rule: Casbin rule list definition to be removed.
142142
143143
Returns:
144144
Number of policies removed.
@@ -172,6 +172,6 @@ def remove_filtered_policy(
172172
for index, value in enumerate(field_values):
173173
query[f"v{index + field_index}"] = value
174174

175-
query["ptype"] = ptype
175+
query["ptype"] = ptype # type: ignore[assignment]
176176
results = self._collection.delete_many(query)
177177
return results.deleted_count > 0

foca/access_control/register_access_control.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
def register_access_control(
3131
cnx_app: App,
32-
mongo_config: MongoConfig,
32+
mongo_config: Optional[MongoConfig],
3333
access_control_config: AccessControlConfig
3434
) -> App:
3535
"""Register access control configuration with flask app.
@@ -58,10 +58,11 @@ def register_access_control(
5858
if mongo_config is None:
5959
mongo_config = MongoConfig()
6060

61+
access_control_db = str(access_control_config.db_name)
6162
if mongo_config.dbs is None:
62-
mongo_config.dbs = {access_control_config.db_name: access_db_conf}
63+
mongo_config.dbs = {access_control_db: access_db_conf}
6364
else:
64-
mongo_config.dbs[access_control_config.db_name] = access_db_conf
65+
mongo_config.dbs[access_control_db] = access_db_conf
6566

6667
cnx_app.app.config.foca.db = mongo_config
6768

@@ -70,7 +71,7 @@ def register_access_control(
7071
app=cnx_app.app,
7172
conf=mongo_config,
7273
db_conf=access_db_conf,
73-
db_name=access_control_config.db_name
74+
db_name=access_control_db
7475
)
7576

7677
# Register access control api specs and corresponding controller.
@@ -129,7 +130,7 @@ def register_permission_specs(
129130
)
130131

131132
app.add_api(
132-
specification=spec.path[0],
133+
specification=spec.path[0], # type: ignore[index]
133134
**spec.dict().get("connexion", {}),
134135
)
135136
return app
@@ -154,12 +155,13 @@ def register_casbin_enforcer(
154155
Connexion application instance with registered casbin configuration.
155156
"""
156157
# Check if default, get package path variables for model.
158+
access_control_config_model = str(access_control_config.model)
157159
if access_control_config.api_specs is None:
158160
casbin_model = str(resource_filename(
159-
ACCESS_CONTROL_BASE_PATH, access_control_config.model
161+
ACCESS_CONTROL_BASE_PATH, access_control_config_model
160162
))
161163
else:
162-
casbin_model = access_control_config.model
164+
casbin_model = access_control_config_model
163165

164166
logger.info("Setting casbin model.")
165167
app.app.config["CASBIN_MODEL"] = casbin_model
@@ -177,7 +179,7 @@ def register_casbin_enforcer(
177179
logger.info("Setting up casbin enforcer.")
178180
adapter = Adapter(
179181
uri=f"mongodb://{mongo_config.host}:{mongo_config.port}/",
180-
dbname=access_control_config.db_name,
182+
dbname=str(access_control_config.db_name),
181183
collection=access_control_config.collection_name
182184
)
183185
app.app.config["casbin_adapter"] = adapter

foca/api/register_openapi.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Register and modify OpenAPI specifications."""
22

33
import logging
4-
from typing import List
4+
from pathlib import Path
5+
from typing import Dict, List
56

67
from connexion import App
78
import yaml
@@ -37,8 +38,9 @@ def register_openapi(
3738
for spec in specs:
3839

3940
# Merge specs
40-
spec_parsed = ConfigParser.merge_yaml(*spec.path)
41-
logger.debug(f"Parsed spec: {spec.path}")
41+
list_specs = [spec.path] if isinstance(spec.path, Path) else spec.path
42+
spec_parsed: Dict = ConfigParser.merge_yaml(*list_specs)
43+
logger.debug(f"Parsed spec: {list_specs}")
4244

4345
# Add/replace root objects
4446
if spec.append is not None:

foca/config/config_parser.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def parse_yaml(conf: Path) -> Dict:
109109
) from exc
110110

111111
@staticmethod
112-
def merge_yaml(*args: Path) -> Optional[Dict]:
112+
def merge_yaml(*args: Path) -> Dict:
113113
"""Parse and merge a set of YAML files.
114114
115115
Merging is done iteratively, from the first, second to the n-th
@@ -126,7 +126,7 @@ def merge_yaml(*args: Path) -> Optional[Dict]:
126126
"""
127127
args_list = list(args)
128128
if not args_list:
129-
return None
129+
return {}
130130
yaml_dict = Addict(ConfigParser.parse_yaml(args_list.pop(0)))
131131

132132
for arg in args_list:
@@ -169,7 +169,8 @@ def parse_custom_config(self, model: str) -> BaseModel:
169169
f"has no class {model_class} or could not be imported"
170170
)
171171
try:
172-
custom_config = model_class(**self.config.custom)
172+
custom_config = model_class( # type: ignore[operator]
173+
**self.config.custom) # type: ignore[attr-defined]
173174
except Exception as exc:
174175
raise ValueError(
175176
"failed validating custom configuration: provided custom "

foca/errors/exceptions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ def _problem_handler_json(exception: Exception) -> Response:
211211
JSON-formatted error response.
212212
"""
213213
# Look up exception & get status code
214-
conf = current_app.config.foca.exceptions
214+
conf = current_app.config.foca.exceptions # type: ignore[attr-defined]
215215
exc = type(exception)
216216
if exc not in conf.mapping:
217217
exc = Exception

foca/factories/celery_app.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def create_celery_app(app: Flask) -> Celery:
1919
Returns:
2020
Celery application instance.
2121
"""
22-
conf = app.config.foca.jobs
22+
conf = app.config.foca.jobs # type: ignore[attr-defined]
2323

2424
# Instantiate Celery app
2525
celery = Celery(
@@ -32,7 +32,7 @@ def create_celery_app(app: Flask) -> Celery:
3232
logger.debug(f"Celery app created from '{calling_module}'.")
3333

3434
# Update Celery app configuration with Flask app configuration
35-
setattr(celery.conf, 'foca', app.config.foca)
35+
setattr(celery.conf, 'foca', app.config.foca) # type: ignore[attr-defined]
3636
logger.debug('Celery app configured.')
3737

3838
class ContextTask(celery.Task): # type: ignore

foca/foca.py

-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ def create_app(self) -> App:
149149
conf.access_control.api_specs or
150150
conf.access_control.api_controllers
151151
):
152-
conf.access_control = None
153152
logger.error(
154153
"Please enable security config to register "
155154
"access control."

0 commit comments

Comments
 (0)