diff --git a/.github/workflows/validator_format.yml b/.github/workflows/validator_format.yml new file mode 100644 index 000000000..32330e156 --- /dev/null +++ b/.github/workflows/validator_format.yml @@ -0,0 +1,32 @@ +name: Format Validator + +on: + push: + paths: + - 'validator/**' + +jobs: + main: + name: Run Black + runs-on: ubuntu-latest + + defaults: + run: + working-directory: validator + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + pip3 install -r requirements.txt + + - name: Run Black + run: | + black --check . diff --git a/.github/workflows/validator_test.yml b/.github/workflows/validator_test.yml index a410dc8b9..7a284f982 100644 --- a/.github/workflows/validator_test.yml +++ b/.github/workflows/validator_test.yml @@ -17,7 +17,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - uses: actions/setup-python@v4 + - name: Set up Python + uses: actions/setup-python@v4 with: python-version: '3.11' diff --git a/README.md b/README.md index a87853585..fc722dc74 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ npm install cd validator python3.11 -m venv venv # Create virtual environment source venv/bin/activate # Use virtual enviornment + #venv/Scripts/activate on Windows pip install -r requirements.txt # Install dependencies flask --app api run ``` diff --git a/validator/core/matchers.py b/validator/core/matchers.py index 143a2dc19..858313d13 100644 --- a/validator/core/matchers.py +++ b/validator/core/matchers.py @@ -277,5 +277,5 @@ def list_matcher_requirements(matcher: Matcher) -> list[str]: Only supports NameList atm """ if not isinstance(matcher, NameListMatcher): - raise NotImplemented + raise NotImplementedError return [course for course in matcher.name_list] diff --git a/validator/core/parser.py b/validator/core/parser.py index c1df44d8f..9a0755e55 100644 --- a/validator/core/parser.py +++ b/validator/core/parser.py @@ -1,4 +1,4 @@ -""" Parser for *.req files.""" +"""Parser for *.req files.""" from core.requirement import Requirement from core.matchers import Matcher diff --git a/validator/core/requirement.py b/validator/core/requirement.py index aad83327a..d341ee791 100644 --- a/validator/core/requirement.py +++ b/validator/core/requirement.py @@ -1,4 +1,5 @@ """Requirement represents a degree requirements.""" + from utils import hashable from core import matchers diff --git a/validator/degree_solver.py b/validator/degree_solver.py index 10e0533b8..f6272ce40 100644 --- a/validator/degree_solver.py +++ b/validator/degree_solver.py @@ -334,7 +334,7 @@ def __str__(self) -> str: def format_core_reqs( - reqs: dict[str, core.store.AssignmentStoreJSON] + reqs: dict[str, core.store.AssignmentStoreJSON], ) -> list[AbstractRequirement]: core_reqs = [] for req_name, req_info in reqs.items(): diff --git a/validator/gen_schema.py b/validator/gen_schema.py index 4cc3b6752..1531f0bc2 100644 --- a/validator/gen_schema.py +++ b/validator/gen_schema.py @@ -166,7 +166,7 @@ def requirement_schema_to_empty_json_body(s: dict[str, Any]) -> dict[str, Any]: snips = dict( zip( - list(map(lambda curr: curr["matcher"]["const"], requirement_schemas)), # type: ignore + list(map(lambda curr: curr["matcher"]["const"], requirement_schemas)), list( map( lambda curr: { @@ -186,7 +186,7 @@ def requirement_schema_to_empty_json_body(s: dict[str, Any]) -> dict[str, Any]: ) ) -snips["$comment"] = f"Generated by {path.basename(__file__)} on {datetime.now()}" # type: ignore +snips["$comment"] = f"Generated by {path.basename(__file__)} on {datetime.now()}" with open(".vscode/major.schema.json", "w") as f: f.write(dumps(schema, indent=2)) diff --git a/validator/major/requirements/shared.py b/validator/major/requirements/shared.py index f9e945af6..cf3ba8481 100644 --- a/validator/major/requirements/shared.py +++ b/validator/major/requirements/shared.py @@ -1,4 +1,4 @@ -""" Definitions for shared requirements """ +"""Definitions for shared requirements""" from __future__ import annotations import json @@ -330,9 +330,9 @@ def __init__( ) -> None: self.required_hours = required_hours self.requirements = requirements - self.valid_courses: dict[ - str, int - ] = valid_courses # Stores map of course & # hours fulfilled (i.e. {"CS 1200": 2}). This is done due to course splitting (1 course can be used to satisfy multiple requirements) + self.valid_courses: dict[str, int] = ( + valid_courses # Stores map of course & # hours fulfilled (i.e. {"CS 1200": 2}). This is done due to course splitting (1 course can be used to satisfy multiple requirements) + ) self.metadata: dict[str, Any] = metadata self.override_filled = False diff --git a/validator/major/tests/edge_cases/test_arts_technology_emerging_communication.py b/validator/major/tests/edge_cases/test_arts_technology_emerging_communication.py index 17948737e..f0f7d062b 100644 --- a/validator/major/tests/edge_cases/test_arts_technology_emerging_communication.py +++ b/validator/major/tests/edge_cases/test_arts_technology_emerging_communication.py @@ -1 +1 @@ -# TODO: implement \ No newline at end of file +# TODO: implement diff --git a/validator/requirements.txt b/validator/requirements.txt index 398743593..c10bde085 100644 --- a/validator/requirements.txt +++ b/validator/requirements.txt @@ -1,56 +1,80 @@ -absl-py==1.2.0 -async-timeout==4.0.2 -attrs==22.2.0 -beautifulsoup4==4.12.0 -black==23.1.0 -certifi==2023.7.22 -charset-normalizer==2.1.1 -check-jsonschema==0.22.0 -click==8.1.3 +absl-py==2.3.0 +annotated-types==0.7.0 +async-timeout==5.0.1 +attrs==25.3.0 +beautifulsoup4==4.13.4 +black==25.1.0 +blinker==1.9.0 +certifi==2025.4.26 +charset-normalizer==3.4.2 +check-jsonschema==0.33.0 +click==8.2.1 +colorama==0.4.6 commonmark==0.9.1 -Deprecated==1.2.13 -exceptiongroup==1.1.0 -Flask==2.2.5 -Flask-Cors==3.0.10 -Flask-Limiter==2.9.2 -gunicorn==20.1.0 -idna==3.4 -iniconfig==2.0.0 -itsdangerous==2.1.2 -Jinja2==3.1.3 -jira==3.5.2 -jsonschema==4.17.3 -limits==2.8.0 -lxml==4.9.3 -MarkupSafe==2.1.1 -mypy==1.0.0 -mypy-extensions==0.4.3 -numpy==1.23.4 +defusedxml==0.7.1 +Deprecated==1.2.18 +exceptiongroup==1.3.0 +Flask==3.1.1 +flask-cors==6.0.0 +Flask-Limiter==3.12 +gunicorn==23.0.0 +idna==3.10 +immutabledict==4.2.1 +iniconfig==2.1.0 +itsdangerous==2.2.0 +Jinja2==3.1.6 +jira==3.8.0 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 +limits==5.2.0 +lxml==5.4.0 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +mypy==1.16.0 +mypy_extensions==1.1.0 +numpy==2.2.6 +oauthlib==3.2.2 ordered-set==4.1.0 -ortools==9.7.2996 -packaging==22.0 -pathspec==0.10.3 -platformdirs==2.6.0 -pluggy==1.0.0 -protobuf==4.23.3 -pydantic==1.10.2 -Pygments==2.15.0 -pyparsing==3.0.9 -pyrsistent==0.19.3 -pytest==7.2.1 -python-dotenv==0.21.0 -redis==4.4.4 -requests==2.28.1 -rich==12.6.0 -ruamel.yaml==0.17.21 -ruamel.yaml.clib==0.2.7 -six==1.16.0 -tomli==2.0.1 -types-beautifulsoup4==4.12.0.6 -types-Flask-Cors==3.0.10.2 -types-jsonschema==4.17.0.6 -types-requests==2.31.0.2 -typing_extensions==4.4.0 -urllib3==1.26.18 -Werkzeug==3.0.1 -wrapt==1.14.1 +ortools==9.12.4544 +packaging==25.0 +pandas==2.2.3 +pathspec==0.12.1 +pillow==11.2.1 +platformdirs==4.3.8 +pluggy==1.6.0 +protobuf==5.29.5 +pydantic==2.11.5 +pydantic_core==2.33.2 +Pygments==2.19.1 +pyparsing==3.2.3 +pyrsistent==0.20.0 +pytest==8.4.0 +python-dateutil==2.9.0.post0 +python-dotenv==1.1.0 +pytz==2025.2 +redis==6.2.0 +referencing==0.36.2 +regress==2025.5.1 +requests==2.32.3 +requests-oauthlib==2.0.0 +requests-toolbelt==1.0.0 +rich==13.9.4 +rpds-py==0.25.1 +ruamel.yaml==0.18.12 +ruamel.yaml.clib==0.2.12 +six==1.17.0 +soupsieve==2.7 +tomli==2.2.1 +types-beautifulsoup4==4.12.0.20250516 +types-Flask-Cors==6.0.0.20250520 +types-html5lib==1.1.11.20250516 +types-jsonschema==4.24.0.20250528 +types-requests==2.32.0.20250602 +types-urllib3==1.26.25.14 +typing-inspection==0.4.1 +typing_extensions==4.14.0 +tzdata==2025.2 +urllib3==2.4.0 +Werkzeug==3.1.3 +wrapt==1.17.2 diff --git a/validator/utils/hashable.py b/validator/utils/hashable.py index 5fe089ee6..344376f65 100644 --- a/validator/utils/hashable.py +++ b/validator/utils/hashable.py @@ -9,7 +9,7 @@ class NameDefinedClass(ABC): def __eq__(self, other: object) -> bool: if not isinstance(other, NameDefinedClass): - raise NotImplemented + raise NotImplementedError return self.name == other.name def __lt__(self, other: NameDefinedClass) -> bool: