Skip to content

Commit da613be

Browse files
author
Kareem Zidane
committedApr 10, 2021
fix pylint errors
1 parent 62b9982 commit da613be

File tree

8 files changed

+126
-103
lines changed

8 files changed

+126
-103
lines changed
 

‎src/cs50/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
from ._logger import _setup_logger
2-
_setup_logger()
1+
"""Exposes API, wraps flask, and sets up logging"""
32

43
from .cs50 import get_float, get_int, get_string
5-
from . import flask
64
from .sql import SQL
5+
from ._logger import _setup_logger
6+
from ._flask import _wrap_flask
7+
8+
_setup_logger()
9+
_wrap_flask()

‎src/cs50/_flask.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Hooks into flask importing to support X-Forwarded-Proto header in online IDE"""
2+
3+
import os
4+
import pkgutil
5+
import sys
6+
7+
from distutils.version import StrictVersion
8+
from werkzeug.middleware.proxy_fix import ProxyFix
9+
10+
11+
def _wrap_flask():
12+
if "flask" in sys.modules:
13+
_support_x_forwarded_proto(sys.modules["flask"])
14+
else:
15+
flask_loader = pkgutil.get_loader('flask')
16+
if flask_loader:
17+
_exec_module_before = flask_loader.exec_module
18+
19+
def _exec_module_after(*args, **kwargs):
20+
_exec_module_before(*args, **kwargs)
21+
_support_x_forwarded_proto(sys.modules["flask"])
22+
23+
flask_loader.exec_module = _exec_module_after
24+
25+
26+
def _support_x_forwarded_proto(flask_module):
27+
if flask_module is None:
28+
return
29+
30+
if flask_module.__version__ < StrictVersion("1.0"):
31+
return
32+
33+
if os.getenv("CS50_IDE_TYPE") == "online":
34+
_flask_init_before = flask_module.Flask.__init__
35+
def _flask_init_after(self, *args, **kwargs):
36+
_flask_init_before(self, *args, **kwargs)
37+
self.wsgi_app = ProxyFix(self.wsgi_app, x_proto=1) # For HTTPS-to-HTTP proxy
38+
flask_module.Flask.__init__ = _flask_init_after

‎src/cs50/_logger.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Sets up logging for cs50 library"""
2+
13
import logging
24
import os.path
35
import re
@@ -14,7 +16,8 @@ def _setup_logger():
1416

1517
try:
1618
# Patch formatException
17-
logging.root.handlers[0].formatter.formatException = lambda exc_info: _formatException(*exc_info)
19+
formatter = logging.root.handlers[0].formatter
20+
formatter.formatException = lambda exc_info: _format_exception(*exc_info)
1821
except IndexError:
1922
pass
2023

@@ -29,14 +32,15 @@ def _setup_logger():
2932
handler.setLevel(logging.DEBUG)
3033

3134
formatter = logging.Formatter("%(levelname)s: %(message)s")
32-
formatter.formatException = lambda exc_info: _formatException(*exc_info)
35+
formatter.formatException = lambda exc_info: _format_exception(*exc_info)
3336
handler.setFormatter(formatter)
3437
_logger.addHandler(handler)
3538

36-
sys.excepthook = lambda type, value, tb: print(_formatException(type, value, tb), file=sys.stderr)
39+
sys.excepthook = lambda type_, value, exc_tb: print(
40+
_format_exception(type_, value, exc_tb), file=sys.stderr)
3741

3842

39-
def _formatException(type, value, tb):
43+
def _format_exception(type_, value, exc_tb):
4044
"""
4145
Format traceback, darkening entries from global site-packages directories
4246
and user-specific site-packages directory.
@@ -48,11 +52,12 @@ def _formatException(type, value, tb):
4852

4953
# Highlight lines not referring to files in site-packages
5054
lines = []
51-
for line in traceback.format_exception(type, value, tb):
55+
for line in traceback.format_exception(type_, value, exc_tb):
5256
matches = re.search(r"^ File \"([^\"]+)\", line \d+, in .+", line)
5357
if matches and matches.group(1).startswith(packages):
5458
lines += line
5559
else:
5660
matches = re.search(r"^(\s*)(.*?)(\s*)$", line, re.DOTALL)
57-
lines.append(matches.group(1) + termcolor.colored(matches.group(2), "yellow") + matches.group(3))
61+
lines.append(
62+
matches.group(1) + termcolor.colored(matches.group(2), "yellow") + matches.group(3))
5863
return "".join(lines).rstrip()

‎src/cs50/_session.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
"""Wraps a SQLAlchemy scoped session"""
2+
13
import os
4+
import sqlite3
25

36
import sqlalchemy
47
import sqlalchemy.orm
5-
import sqlite3
68

79
class Session:
810
"""Wraps a SQLAlchemy scoped session"""
@@ -14,6 +16,8 @@ def __init__(self, url, **engine_kwargs):
1416

1517

1618
def execute(self, statement):
19+
"""Converts statement to str and executes it"""
20+
# pylint: disable=no-member
1721
return self._session.execute(sqlalchemy.text(str(statement)))
1822

1923

‎src/cs50/_statement.py

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Parses a SQL statement and binds its parameters"""
2+
13
import collections
24
import datetime
35
import enum
@@ -8,6 +10,7 @@
810

911

1012
class Statement:
13+
"""Parses and binds a SQL statement"""
1114
def __init__(self, dialect, sql, *args, **kwargs):
1215
if len(args) > 0 and len(kwargs) > 0:
1316
raise RuntimeError("cannot pass both positional and named parameters")
@@ -21,13 +24,14 @@ def __init__(self, dialect, sql, *args, **kwargs):
2124
self._command = self._parse_command()
2225
self._tokens = self._bind_params()
2326

27+
2428
def _parse(self):
2529
formatted_statements = sqlparse.format(self._sql, strip_comments=True).strip()
2630
parsed_statements = sqlparse.parse(formatted_statements)
2731
statement_count = len(parsed_statements)
2832
if statement_count == 0:
2933
raise RuntimeError("missing statement")
30-
elif statement_count > 1:
34+
if statement_count > 1:
3135
raise RuntimeError("too many statements at once")
3236

3337
return parsed_statements[0]
@@ -49,11 +53,11 @@ def _parse_command(self):
4953
def _bind_params(self):
5054
tokens = self._tokenize()
5155
paramstyle, placeholders = self._parse_placeholders(tokens)
52-
if paramstyle in {Paramstyle.FORMAT, Paramstyle.QMARK}:
56+
if paramstyle in {_Paramstyle.FORMAT, _Paramstyle.QMARK}:
5357
tokens = self._bind_format_or_qmark(placeholders, tokens)
54-
elif paramstyle == Paramstyle.NUMERIC:
58+
elif paramstyle == _Paramstyle.NUMERIC:
5559
tokens = self._bind_numeric(placeholders, tokens)
56-
if paramstyle in {Paramstyle.NAMED, Paramstyle.PYFORMAT}:
60+
if paramstyle in {_Paramstyle.NAMED, _Paramstyle.PYFORMAT}:
5761
tokens = self._bind_named_or_pyformat(placeholders, tokens)
5862

5963
tokens = _escape_verbatim_colons(tokens)
@@ -86,9 +90,9 @@ def _parse_placeholders(self, tokens):
8690
def _default_paramstyle(self):
8791
paramstyle = None
8892
if self._args:
89-
paramstyle = Paramstyle.QMARK
93+
paramstyle = _Paramstyle.QMARK
9094
elif self._kwargs:
91-
paramstyle = Paramstyle.NAMED
95+
paramstyle = _Paramstyle.NAMED
9296

9397
return paramstyle
9498

@@ -118,8 +122,10 @@ def _bind_numeric(self, placeholders, tokens):
118122
unused_arg_indices.remove(num)
119123

120124
if len(unused_arg_indices) > 0:
121-
unused_args = ", ".join([str(self._escape(self._args[i])) for i in sorted(unused_arg_indices)])
122-
raise RuntimeError(f"unused value{'' if len(unused_arg_indices) == 1 else 's'} ({unused_args})")
125+
unused_args = ", ".join(
126+
[str(self._escape(self._args[i])) for i in sorted(unused_arg_indices)])
127+
raise RuntimeError(
128+
f"unused value{'' if len(unused_arg_indices) == 1 else 's'} ({unused_args})")
123129

124130
return tokens
125131

@@ -134,7 +140,9 @@ def _bind_named_or_pyformat(self, placeholders, tokens):
134140
unused_params.remove(param_name)
135141

136142
if len(unused_params) > 0:
137-
raise RuntimeError("unused value{'' if len(unused_params) == 1 else 's'} ({', '.join(sorted(unused_params))})")
143+
joined_unused_params = ", ".join(sorted(unused_params))
144+
raise RuntimeError(
145+
f"unused value{'' if len(unused_params) == 1 else 's'} ({joined_unused_params})")
138146

139147
return tokens
140148

@@ -144,7 +152,7 @@ def _escape(self, value):
144152
Escapes value using engine's conversion function.
145153
https://docs.sqlalchemy.org/en/latest/core/type_api.html#sqlalchemy.types.TypeEngine.literal_processor
146154
"""
147-
155+
# pylint: disable=too-many-return-statements
148156
if isinstance(value, (list, tuple)):
149157
return self._escape_iterable(value)
150158

@@ -163,20 +171,18 @@ def _escape(self, value):
163171

164172
raise RuntimeError(f"unsupported value: {value}")
165173

174+
string_processor = sqlalchemy.types.String().literal_processor(self._dialect)
166175
if isinstance(value, datetime.date):
167176
return sqlparse.sql.Token(
168-
sqlparse.tokens.String,
169-
sqlalchemy.types.String().literal_processor(self._dialect)(value.strftime("%Y-%m-%d")))
177+
sqlparse.tokens.String, string_processor(value.strftime("%Y-%m-%d")))
170178

171179
if isinstance(value, datetime.datetime):
172180
return sqlparse.sql.Token(
173-
sqlparse.tokens.String,
174-
sqlalchemy.types.String().literal_processor(self._dialect)(value.strftime("%Y-%m-%d %H:%M:%S")))
181+
sqlparse.tokens.String, string_processor(value.strftime("%Y-%m-%d %H:%M:%S")))
175182

176183
if isinstance(value, datetime.time):
177184
return sqlparse.sql.Token(
178-
sqlparse.tokens.String,
179-
sqlalchemy.types.String().literal_processor(self._dialect)(value.strftime("%H:%M:%S")))
185+
sqlparse.tokens.String, string_processor(value.strftime("%H:%M:%S")))
180186

181187
if isinstance(value, float):
182188
return sqlparse.sql.Token(
@@ -189,9 +195,7 @@ def _escape(self, value):
189195
sqlalchemy.types.Integer().literal_processor(self._dialect)(value))
190196

191197
if isinstance(value, str):
192-
return sqlparse.sql.Token(
193-
sqlparse.tokens.String,
194-
sqlalchemy.types.String().literal_processor(self._dialect)(value))
198+
return sqlparse.sql.Token(sqlparse.tokens.String, string_processor(value))
195199

196200
if value is None:
197201
return sqlparse.sql.Token(
@@ -207,6 +211,7 @@ def _escape_iterable(self, iterable):
207211

208212

209213
def get_command(self):
214+
"""Returns statement command (e.g., SELECT) or None"""
210215
return self._command
211216

212217

@@ -220,41 +225,42 @@ def _is_placeholder(token):
220225

221226
def _parse_placeholder(token):
222227
if token.value == "?":
223-
return Paramstyle.QMARK, None
228+
return _Paramstyle.QMARK, None
224229

225230
# E.g., :1
226231
matches = re.search(r"^:([1-9]\d*)$", token.value)
227232
if matches:
228-
return Paramstyle.NUMERIC, int(matches.group(1)) - 1
233+
return _Paramstyle.NUMERIC, int(matches.group(1)) - 1
229234

230235
# E.g., :foo
231236
matches = re.search(r"^:([a-zA-Z]\w*)$", token.value)
232237
if matches:
233-
return Paramstyle.NAMED, matches.group(1)
238+
return _Paramstyle.NAMED, matches.group(1)
234239

235240
if token.value == "%s":
236-
return Paramstyle.FORMAT, None
241+
return _Paramstyle.FORMAT, None
237242

238243
# E.g., %(foo)s
239244
matches = re.search(r"%\((\w+)\)s$", token.value)
240245
if matches:
241-
return Paramstyle.PYFORMAT, matches.group(1)
246+
return _Paramstyle.PYFORMAT, matches.group(1)
242247

243248
raise RuntimeError(f"{token.value}: invalid placeholder")
244249

245250

246251
def _escape_verbatim_colons(tokens):
247252
for token in tokens:
248253
if _is_string_literal(token):
249-
token.value = re.sub("(^'|\s+):", r"\1\:", token.value)
254+
token.value = re.sub(r"(^'|\s+):", r"\1\:", token.value)
250255
elif _is_identifier(token):
251-
token.value = re.sub("(^\"|\s+):", r"\1\:", token.value)
256+
token.value = re.sub(r"(^\"|\s+):", r"\1\:", token.value)
252257

253258
return tokens
254259

255260

256261
def _is_command_token(token):
257-
return token.ttype in {sqlparse.tokens.Keyword, sqlparse.tokens.Keyword.DDL, sqlparse.tokens.Keyword.DML}
262+
return token.ttype in {
263+
sqlparse.tokens.Keyword, sqlparse.tokens.Keyword.DDL, sqlparse.tokens.Keyword.DML}
258264

259265

260266
def _is_string_literal(token):
@@ -265,7 +271,7 @@ def _is_identifier(token):
265271
return token.ttype == sqlparse.tokens.Literal.String.Symbol
266272

267273

268-
class Paramstyle(enum.Enum):
274+
class _Paramstyle(enum.Enum):
269275
FORMAT = enum.auto()
270276
NAMED = enum.auto()
271277
NUMERIC = enum.auto()

0 commit comments

Comments
 (0)