Skip to content

Commit 4b9c051

Browse files
yammesickagal432
andauthored
fix: Bump old deps, fix tests (#361)
* fix: Bump old deps, fix tests * fix: Update Flake8 autochecker * fix: pytest's spaces format * ci: Update cr-checks.yml * chore: Pin deps * chore: Bump some more versions in CI/CD * fix: flake8 errors (until we merge this huge diff) * fix: Hopefully we're done (temp. disable git) Co-authored-by: Gal Singer <gal432@gmail.com>
1 parent 89abbf3 commit 4b9c051

27 files changed

+577
-422
lines changed

.github/workflows/cr-checks.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ on:
99
jobs:
1010
build:
1111

12-
runs-on: ubuntu-18.04
12+
runs-on: ubuntu-latest
1313

1414
steps:
1515
- uses: actions/checkout@v2
16-
- name: Set up Python 3.8
16+
- name: Set up Python 3.12
1717
uses: actions/setup-python@v1
1818
with:
19-
python-version: 3.8
19+
python-version: 3.12
2020
- name: Install dependencies
2121
run: |
2222
python -m pip install --upgrade pip
@@ -27,7 +27,7 @@ jobs:
2727
chmod +x /opt/vnu/vnu-runtime-image/bin/vnu
2828
- name: Lint
2929
run: |
30-
flake8 lms --count --show-source --statistics
30+
flake8 lms --ignore Q000,I202,W503,S101,I100,I101,E800 --import-order-style=google --count --show-source --statistics
3131
- name: Test
3232
run: |
3333
export PYTHONPATH=`pwd`

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,4 @@ lms/lmsweb/config.py
136136
db.sqlite
137137
vim.session
138138
devops/rabbitmq.cookie
139+
our.db

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,26 @@
99

1010
👋 Welcome to Python course learning management system. 🐍
1111

12-
The system objectives -
12+
The system objectives -
13+
1314
1. Allow teachers and mentors to input exercises list and provide feedback/comments to students exercises solutions.
1415
2. Allow students to load their exercises solutions and get feedback to their work.
1516

1617
## Creating development environment
18+
1719
### Prerequisites
20+
1821
1. Linux based system - either [WSL on windows](https://docs.microsoft.com/en-us/windows/wsl/install-win10) or full blown linux.
19-
2. [Python](https://www.python.org/downloads/release/python-385/)
22+
2. [Python](https://www.python.org/downloads/release/python-385/)
2023
3. [Docker](https://docs.docker.com/docker-for-windows/install/) and docker-compose.
2124

25+
2226
### Minimal setup
23-
This setup is for debug purposes and will use sqlite database and frontend only.
27+
28+
This setup is for debug purposes and will use SQLite database and frontend only.
2429

2530
Steps to do:
31+
2632
1. Clone this repository.
2733
2. Set environment variables.
2834
3. Run the application.
@@ -48,7 +54,9 @@ After logging in, use [localhost admin](https://127.0.0.1:5000/admin) to modify
4854

4955

5056
### Full setup
57+
5158
This setup will create the following items:
59+
5260
* Application - LMS code.
5361
* Middleware (messaging queue) - RabbitMQ.
5462
* Persistence database - PostgreSQL.
@@ -70,7 +78,8 @@ cd devops
7078
```
7179

7280
In case you want to add the stub data to PostgreSQL DB, run:
73-
```
81+
82+
```bash
7483
docker exec -it lms_http_1 bash
7584
python lmsdb/bootstrap.py
7685
```
@@ -86,17 +95,23 @@ In case you want to enable the mail system:
8695

8796

8897
## Code modification check list
89-
### Run flake8
90-
```
98+
99+
## Run flake8
100+
101+
```bash
91102
# on lms root directory
92103
flake8 lms
93104
```
105+
94106
### Run tests
95-
```
107+
108+
```bash
96109
export PYTHONPATH=`pwd`
97110
pip install -r requirements.txt
98111
pip install -r dev_requirements.txt
99112
py.test -vvv
100113
```
114+
101115
### Contributing
116+
102117
View [contributing guidelines](https://github.com/PythonFreeCourse/lms/blob/master/CONTRIBUTING.md).

dev_requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
debugpy==1.4.3
2-
ipdb==0.13.9
3-
pytest-cov==2.12.1
4-
pytest-env==0.6.2
1+
debugpy==1.8.1
2+
ipdb==0.13.13
3+
pytest-cov==4.1.0
4+
pytest-env==1.1.3

devops/lms.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ services:
2121
- lms
2222

2323
rabbitmq:
24-
image: rabbitmq:3.9-management-alpine
24+
image: rabbitmq:3.12-management-alpine
2525
hostname: celery-mq
2626
volumes:
2727
- rabbit-data-volume:/var/lib/rabbitmq
@@ -130,7 +130,6 @@ volumes:
130130
rabbit-data-volume:
131131
repositories-data-volume:
132132

133-
134133
networks:
135134
lms:
136135
external:

lms/lmsdb/models.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,15 @@ def __str__(self):
247247
@pre_save(sender=User)
248248
def on_save_handler(model_class, instance, created):
249249
"""Hash password on creation/save."""
250+
supported_hashing = ('pbkdf2:sha256', 'scrypt:')
250251

251252
# If password changed then it won't start with hash's method prefix
252-
is_password_changed = not instance.password.startswith('pbkdf2:sha256')
253+
is_password_changed = not instance.password.startswith(supported_hashing)
253254
if created or is_password_changed:
254255
instance.password = generate_password_hash(instance.password)
255256
instance.uuid = uuid4()
256257

257-
is_api_key_changed = not instance.api_key.startswith('pbkdf2:sha256')
258+
is_api_key_changed = not instance.api_key.startswith(supported_hashing)
258259
if created or is_api_key_changed:
259260
if not instance.api_key:
260261
instance.api_key = model_class.random_password()
@@ -563,9 +564,7 @@ class Solution(BaseModel):
563564
)
564565

565566
@property
566-
def solution_files(
567-
self,
568-
) -> Union[Iterable['SolutionFile'], 'SolutionFile']:
567+
def solution_files(self) -> Iterable["SolutionFile"]:
569568
return SolutionFile.filter(SolutionFile.solution == self)
570569

571570
@property
@@ -707,8 +706,8 @@ def create_solution(
707706
raise AlreadyExists('This solution already exists.')
708707

709708
instance = cls.create(**{
710-
cls.exercise.name: exercise,
711-
cls.solver.name: solver,
709+
cls.exercise.name: exercise.id,
710+
cls.solver.name: solver.id,
712711
cls.submission_timestamp.name: datetime.now(),
713712
cls.hashed.name: hash_,
714713
})
@@ -1135,7 +1134,7 @@ def generate_string(
11351134

11361135

11371136
def create_demo_users() -> None:
1138-
print('First run! Here are some users to get start with:') # noqa: T001
1137+
print('First run! Here are some users to get start with:') # noqa: T201
11391138
fields = ['username', 'fullname', 'mail_address', 'role']
11401139
student_role = Role.by_name('Student')
11411140
admin_role = Role.by_name('Administrator')
@@ -1149,7 +1148,7 @@ def create_demo_users() -> None:
11491148
password = User.random_password()
11501149
api_key = User.random_password(stronger=True)
11511150
User.create(**user, password=password, api_key=api_key)
1152-
print(f"User: {user['username']}, Password: {password}") # noqa: T001
1151+
print(f"User: {user['username']}, Password: {password}") # noqa: T201
11531152

11541153

11551154
def create_basic_roles() -> None:

lms/lmstests/public/unittests/import_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@ def load_test_from_module(file_path: str):
6363

6464
if __name__ == '__main__':
6565
if len(sys.argv) != 2:
66-
print('python load_tests.py test-module-path') # noqa: T001
66+
print('python load_tests.py test-module-path') # noqa: T201
6767
load_tests_from_path(file_path=sys.argv[1])

lms/lmstests/public/unittests/services.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import subprocess # noqa: S404
33
from typing import Iterable, List, Optional
4+
45
from flask_babel import gettext as _ # type: ignore
56
import junitparser
67
from junitparser.junitparser import TestCase

lms/lmstests/sandbox/linters/defines.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
'C404': 'אין סיבה להשתמש פה ב־list comprehension – השתמשו ב־dictionary comprehension במקום.',
1616
'C406': 'אין סיבה להשתמש בפונקציה <code>list</code> או <code>tuple</code>. אפשר במקום להשתמש פשוט בסוגריים המתאימים שמייצגים את מבנה הנתונים.',
1717
'C407': 'לא חייבים להשתמש כאן ב־list/dict comprehension, הפונקציה יודעת לקבל גנרטור.',
18-
'C408': 'אין סיבה לקרוא פה לפונקציה – עדיף לציין מבנה נתונים ריק. במקום <code dir="ltr">dict()</code>, לדוגמה, רשמו <code dir="ltr">\{\}</code>.', # NOQA: W605
18+
'C408': 'אין סיבה לקרוא פה לפונקציה – עדיף לציין מבנה נתונים ריק. במקום <code dir="ltr">dict()</code>, לדוגמה, רשמו <code dir="ltr">{}</code>.', # NOQA: W605
1919
'C409': 'העברת ל־<code dir="ltr">tuple()</code> רשימה, אך אין בזה צורך. עדיף להשתמש בסוגריים עגולים במקום.',
2020
'C410': 'אין צורך להמיר את הרשימה הזו ל־list. הוציאו אותה מהקריאה לפונקציה.',
2121
'C413': 'אין צורך להמיר לרשימה, <var>sorted</var> כבר מחזירה רשימה בעצמה.',
@@ -135,6 +135,7 @@
135135
'S322', # input is a dangerous method of Python 2 yada yada
136136
'T000', # todo note found
137137
'T001', # print found
138+
'T201', # print found (newer version of T001)
138139
'T002', # Python 2.x reserved word print used
139140
'W291', # whitespaces @ end of line
140141
'W292', # no new line in the end of the code

lms/lmstests/sandbox/linters/python.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
from collections.abc import Iterable
12
import tempfile
2-
import typing
3+
from typing import cast
34

45
from flake8.main import application
56

@@ -10,7 +11,7 @@
1011
class PythonLinter(BaseLinter):
1112
def initialize(self):
1213
self._app = application.Application()
13-
self._app.initialize(argv=['--import-order-style', 'google'])
14+
self._argv = ['--import-order-style', 'google']
1415

1516
@property
1617
def app(self) -> application.Application:
@@ -24,19 +25,22 @@ def get_error_text(self, error: LinterError) -> str:
2425
def match_to_file_suffix(file_suffix: str) -> bool:
2526
return file_suffix.lower() == 'py'
2627

27-
def _get_errors_from_solution(self) -> typing.Iterable[LinterError]:
28-
index_of_check = 0
28+
def _get_errors_from_solution(self) -> Iterable[LinterError]:
2929
with tempfile.NamedTemporaryFile('w') as temp_file:
3030
temp_file.write(self._code)
3131
temp_file.flush()
32-
33-
self.app.run_checks([temp_file.name])
34-
checkers = self.app.file_checker_manager.checkers
35-
results = checkers[index_of_check].results
32+
self.app.initialize(argv=[temp_file.name, *self._argv])
33+
self.app.run_checks()
34+
assert self.app.file_checker_manager is not None
35+
artifacts = self.app.file_checker_manager.results
36+
results = [r for _, results_, _ in artifacts for r in results_]
3637

3738
for result in results:
39+
assert isinstance(result, tuple)
40+
result = cast(tuple, result)
3841
response = LinterError(
39-
*result, solution_file_id=self._solution_file_id)
42+
*result, solution_file_id=self._solution_file_id,
43+
)
4044
if response.error_code in defines.FLAKE_SKIP_ERRORS:
4145
self._logger.info(
4246
'Skipping error %s on line %s to solution file %s',

lms/lmstests/sandbox/linters/sql.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212

1313
class SQLLinter(BaseLinter):
1414
def initialize(self):
15-
self._app = Linter(config=FluffConfig.from_root())
15+
config = {'dialect': 'ansi'}
16+
self._app = Linter(config=FluffConfig(overrides=config))
1617

1718
@property
1819
def app(self) -> Linter:

lms/lmsweb/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@
3232

3333
http_basic_auth = HTTPBasicAuth()
3434

35-
limiter = Limiter(webapp, key_func=get_remote_address)
35+
limiter = Limiter(
36+
app=webapp,
37+
key_func=get_remote_address,
38+
default_limits=["60 per minute"],
39+
storage_uri='memory://',
40+
)
3641

3742

3843
if not config_file.exists():

lms/lmsweb/git_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def _build_upload_operation(self) -> _GitOperation:
200200
contain_new_commits=False,
201201
)
202202

203-
def _load_files_from_repository(self) -> typing.List[upload.File]:
203+
def _load_files_from_repository(self) -> list[upload.File]:
204204
"""
205205
Since the remote server is a git bare repository
206206
we need to 'clone' the bare repository to resolve the files.
@@ -209,7 +209,7 @@ def _load_files_from_repository(self) -> typing.List[upload.File]:
209209
"""
210210
with tempfile.TemporaryDirectory() as tempdir:
211211
self._execute_command(
212-
args=['git', 'clone', self.repository_folder, '.'],
212+
args=['git', 'clone', str(self.repository_folder), '.'],
213213
cwd=tempdir,
214214
)
215215
to_return = []

lms/lmsweb/tools/validators.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1+
from typing import TYPE_CHECKING
2+
13
from flask_babel import gettext as _ # type: ignore
2-
from wtforms.fields.core import StringField
4+
from wtforms import StringField
35
from wtforms.validators import ValidationError
46

57
from lms.lmsdb.models import User
68

9+
if TYPE_CHECKING:
10+
from lms.lmsweb.forms.register import RegisterForm
11+
from lms.lmsweb.forms.reset_password import ResetPassForm
12+
713

8-
def UniqueUsernameRequired(
9-
_form: 'RegisterForm', field: StringField, # type: ignore # NOQA: F821
10-
) -> None:
14+
def UniqueUsernameRequired(__: 'RegisterForm', field: StringField) -> None:
1115
username_exists = User.get_or_none(User.username == field.data)
1216
if username_exists:
1317
raise ValidationError(_('The username is already in use'))
1418

1519

16-
def UniqueEmailRequired(
17-
_form: 'RegisterForm', field: StringField, # type: ignore # NOQA: F821
18-
) -> None:
20+
def UniqueEmailRequired(__: 'RegisterForm', field: StringField) -> None:
1921
email_exists = User.get_or_none(User.mail_address == field.data)
2022
if email_exists:
2123
raise ValidationError(_('The email is already in use'))
2224

2325

24-
def EmailNotExists(
25-
_form: 'ResetPassForm', field: StringField, # type: ignore # NOQA: F821
26-
) -> None:
26+
def EmailNotExists(__: 'ResetPassForm', field: StringField) -> None:
2727
email_exists = User.get_or_none(User.mail_address == field.data)
2828
if not email_exists:
2929
raise ValidationError(_('Invalid email'))

0 commit comments

Comments
 (0)