From 70694c70703e3dd7a98f61558cfc70e70bd0d1c1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 21:52:35 +0000 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.2 → v0.17.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.2...v0.17.0) - [github.com/abravalheri/validate-pyproject: v0.24.1 → 0.26](https://github.com/abravalheri/validate-pyproject/compare/v0.24.1...0.26) - [github.com/executablebooks/mdformat: 0.7.22 → 1.0.0](https://github.com/executablebooks/mdformat/compare/0.7.22...1.0.0) - [github.com/igorshubovych/markdownlint-cli: v0.44.0 → v0.49.1](https://github.com/igorshubovych/markdownlint-cli/compare/v0.44.0...v0.49.1) - [github.com/crate-ci/typos: v1 → v1.51.1](https://github.com/crate-ci/typos/compare/v1...v1.51.1) --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bcf415f..f86b13d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.11.2 + rev: v0.17.0 hooks: # Run the linter. - id: ruff @@ -11,12 +11,12 @@ repos: - id: ruff-format - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.24.1 + rev: '0.26' hooks: - id: validate-pyproject - repo: https://github.com/executablebooks/mdformat - rev: 0.7.22 + rev: 1.0.0 hooks: - id: mdformat additional_dependencies: @@ -24,7 +24,7 @@ repos: - mdformat-admon - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.44.0 + rev: v0.49.1 hooks: - id: markdownlint # args: @@ -36,7 +36,7 @@ repos: # ] - repo: https://github.com/crate-ci/typos - rev: v1 + rev: v1.51.1 hooks: - id: typos From ab8bf716332bfdc43c8cebf631cb5b971a02552f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 21:53:56 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .pre-commit-config.yaml | 2 +- README.md | 40 ++++++++++++++++++-------- myrpl_cli/api.py | 11 ++++---- myrpl_cli/main.py | 10 ++++--- myrpl_cli/models.py | 62 +++++++++++++++-------------------------- myrpl_cli/myrpl.py | 11 ++++---- tests/api_test.py | 6 ++-- 7 files changed, 71 insertions(+), 71 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f86b13d..5cc67ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ repos: - id: ruff-format - repo: https://github.com/abravalheri/validate-pyproject - rev: '0.26' + rev: "0.26" hooks: - id: validate-pyproject diff --git a/README.md b/README.md index c1ae862..0beb7f8 100644 --- a/README.md +++ b/README.md @@ -168,18 +168,34 @@ I chose [ruff](https://github.com/astral-sh/ruff/) for linting + formatting ## 🗺️ Roadmap -- \[x\] Implement basic authentication functionality -- \[x\] Fetch course activities -- \[x\] Store credentials securely for reuse -- \[x\] Fetch latest submission -- \[ \] Implement hidden file .pyc download via submission abuse (branch: `feature/hidden_file_decompilation`) -- \[ \] Implement hidden file decompilation for python version agnostic test execution -- \[ \] Implement activity submission (`myrpl submit`) -- \[ \] Implement course/category/activity progress (`myrpl status`) -- \[ \] Remove annoying keyring passphrase -- \[ \] Enhance test coverage -- \[ \] VS Code extension (?) -- \[ \] Add support for additional programming languages (idk if actually necessary) +- \[x\] +- \[x\] +- \[x\] +- \[x\] +- \[ + + \] +- \[ + + \] +- \[ + + \] +- \[ + + \] +- \[ + + \] +- \[ + + \] +- \[ + + \] +- \[ + + \] Please note that this roadmap is subject to change and may be updated based on user feedback and my own time 😁 diff --git a/myrpl_cli/api.py b/myrpl_cli/api.py index a93dde6..4ec268a 100644 --- a/myrpl_cli/api.py +++ b/myrpl_cli/api.py @@ -1,13 +1,12 @@ -from typing import List -import mimetypes import json +import mimetypes import requests from requests_toolbelt.multipart.encoder import MultipartEncoder -from myrpl_cli.errors import MissingCredentialsError -from myrpl_cli.models import Course, Activity, Submission, SubmissionResult from myrpl_cli.credential_manager import CredentialManager +from myrpl_cli.errors import MissingCredentialsError +from myrpl_cli.models import Activity, Course, Submission, SubmissionResult BASE_URL = "https://myrpl.ar" @@ -37,14 +36,14 @@ def login(self, username_or_email, password): self.headers["Authorization"] = f"{login_data['token_type']} {login_data['access_token']}" return login_data - def fetch_courses(self) -> List[Course]: + def fetch_courses(self) -> list[Course]: """Fetches all courses""" courses_response = self.auth_api_call("get", f"{BASE_URL}/api/courses") courses = [Course(**course) for course in courses_response] return courses - def fetch_activities(self, course: Course) -> List[Activity]: + def fetch_activities(self, course: Course) -> list[Activity]: """Fetches all activities in a course""" activities_response = self.auth_api_call("get", f"{BASE_URL}/api/courses/{course.id}/activities") diff --git a/myrpl_cli/main.py b/myrpl_cli/main.py index aaad7c3..f734768 100644 --- a/myrpl_cli/main.py +++ b/myrpl_cli/main.py @@ -1,11 +1,13 @@ -import logging import argparse +import logging + from dotenv import load_dotenv -from myrpl_cli.errors import MissingCredentialsError, NotMyRPLDirectoryError -from myrpl_cli.myrpl import MyRPL + +from myrpl_cli import __version__ from myrpl_cli.api import API from myrpl_cli.credential_manager import CredentialManager -from myrpl_cli import __version__ +from myrpl_cli.errors import MissingCredentialsError, NotMyRPLDirectoryError +from myrpl_cli.myrpl import MyRPL load_dotenv() logging.basicConfig(level=logging.INFO) diff --git a/myrpl_cli/models.py b/myrpl_cli/models.py index ee3310b..b5d0307 100644 --- a/myrpl_cli/models.py +++ b/myrpl_cli/models.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Literal +from typing import Literal from pydantic import BaseModel, computed_field, field_validator @@ -27,8 +27,8 @@ class ActivityMetadata(BaseModel): class MyRPLMetadata(BaseModel): course: CourseMetadata - category: Optional[CategoryMetadata] = None - activity: Optional[ActivityMetadata] = None + category: CategoryMetadata | None = None + activity: ActivityMetadata | None = None class Course(BaseModel): @@ -98,24 +98,16 @@ def category(self) -> Category: description: str language: str - activity_unit_tests: Optional[str] = None + activity_unit_tests: str | None = None file_id: int - submission_status: Optional[ - Literal[ - "PENDING", - "ENQUEUED", - "PROCESSING", - "BUILD_ERROR", - "RUNTIME_ERROR", - "FAILURE", - "SUCCESS", - "TIME_OUT", - ] - ] = None + submission_status: ( + Literal["PENDING", "ENQUEUED", "PROCESSING", "BUILD_ERROR", "RUNTIME_ERROR", "FAILURE", "SUCCESS", "TIME_OUT"] + | None + ) = None @field_validator("submission_status", mode="before") @classmethod - def empty_str_to_none(cls, v: str) -> Optional[str]: + def empty_str_to_none(cls, v: str) -> str | None: """Coerses empty string to None""" if v == "": return None @@ -138,7 +130,7 @@ class UnitTestResult(BaseModel): id: int test_name: str passed: bool - error_messages: Optional[str] = None + error_messages: str | None = None class Submission(BaseModel): @@ -146,7 +138,7 @@ class Submission(BaseModel): @field_validator("submission_status", mode="before") @classmethod - def empty_str_to_none(cls, v: str) -> Optional[str]: + def empty_str_to_none(cls, v: str) -> str | None: """Coerses empty string to None""" if v == "": return None @@ -162,26 +154,18 @@ def empty_str_to_none(cls, v: str) -> Optional[str]: activity_starting_files_type: str activity_starting_files_id: int activity_language: str - activity_unit_tests: Optional[str] = None - submission_status: Optional[ - Literal[ - "PENDING", - "ENQUEUED", - "PROCESSING", - "BUILD_ERROR", - "RUNTIME_ERROR", - "FAILURE", - "SUCCESS", - "TIME_OUT", - ] - ] = None - is_final_solution: Optional[bool] = None - exit_message: Optional[str] = None - stderr: Optional[str] = None - stdout: Optional[str] = None - io_test_run_results: List[dict] = [] - unit_test_run_results: List[UnitTestResult] = [] - submission_date: Optional[str] = None + activity_unit_tests: str | None = None + submission_status: ( + Literal["PENDING", "ENQUEUED", "PROCESSING", "BUILD_ERROR", "RUNTIME_ERROR", "FAILURE", "SUCCESS", "TIME_OUT"] + | None + ) = None + is_final_solution: bool | None = None + exit_message: str | None = None + stderr: str | None = None + stdout: str | None = None + io_test_run_results: list[dict] = [] + unit_test_run_results: list[UnitTestResult] = [] + submission_date: str | None = None class SubmissionResult(Submission): diff --git a/myrpl_cli/myrpl.py b/myrpl_cli/myrpl.py index c738da9..facbd7b 100644 --- a/myrpl_cli/myrpl.py +++ b/myrpl_cli/myrpl.py @@ -1,16 +1,15 @@ -import os -import logging import getpass -from typing import Optional +import logging +import os import pytest import toml from tqdm import tqdm -from myrpl_cli.errors import AuthError, NotMyRPLDirectoryError -from myrpl_cli.models import Activity, MyRPLMetadata from myrpl_cli.api import API from myrpl_cli.credential_manager import CredentialManager +from myrpl_cli.errors import AuthError, NotMyRPLDirectoryError +from myrpl_cli.models import Activity, MyRPLMetadata logger = logging.getLogger(__name__) @@ -106,7 +105,7 @@ def test(self, pytest_args): if meta.activity is not None: logger.info("\t└──Activity: %s", meta.activity.name) - def open_metadata(self) -> Optional[MyRPLMetadata]: + def open_metadata(self) -> MyRPLMetadata | None: """ Reads, parses and returns the current directory's metadata """ diff --git a/tests/api_test.py b/tests/api_test.py index 6c089f8..62ccd46 100644 --- a/tests/api_test.py +++ b/tests/api_test.py @@ -4,10 +4,10 @@ import pytest import requests -from myrpl_cli.errors import MissingCredentialsError -from myrpl_cli.credential_manager import CredentialManager -from myrpl_cli.models import Course, Activity, Submission, SubmissionResult from myrpl_cli.api import API +from myrpl_cli.credential_manager import CredentialManager +from myrpl_cli.errors import MissingCredentialsError +from myrpl_cli.models import Activity, Course, Submission, SubmissionResult @pytest.fixture(name="credential_manager")