diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 000000000..6f545b4ec --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,33 @@ +# Attribution (MIT License): Opentensor Foundation 2026 (https://github.com/opentensor) +# https://github.com/opentensor/bittensor/blob/bb526e6d61210ef2ee0e229f63ca5518381ff5a7/.github/workflows/ruff.yml + +name: Ruff - formatter check +permissions: + contents: read + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +jobs: + ruff: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + + steps: + - name: Install Ruff + uses: astral-sh/ruff-action@v3.6.1 + with: + version: "0.15.7" + checksum: "2253ba7f064023def4b77aaef127756d3724e92a0fc69d666d4692f5c019af6d" + args: + "--version" + + - name: Ruff format check + run: | + ruff format --check --diff + + - name: Ruff lint check + run: | + ruff check + \ No newline at end of file diff --git a/agent.py b/agent.py index 593eb6a00..87d888c2b 100644 --- a/agent.py +++ b/agent.py @@ -1,8 +1,7 @@ -import os import json -import requests - +import os +import requests RUN_ID = os.getenv("RUN_ID") if not RUN_ID: @@ -13,23 +12,17 @@ print("[AGENT] WARNING: SANDBOX_PROXY_URL is not set") - def inference(model, temperature, messages): try: - payload = { - "run_id": RUN_ID, - "model": model, - "temperature": temperature, - "messages": messages - } - - print(f"[AGENT] inference(): Sending inference request for model {model} (temperature {temperature}) with {len(messages)} messages") + payload = {"run_id": RUN_ID, "model": model, "temperature": temperature, "messages": messages} + + print( + f"[AGENT] inference(): Sending inference request for model {model} (temperature {temperature}) with {len(messages)} messages" + ) response = requests.post( - f"{SANDBOX_PROXY_URL}/api/inference", - headers={"Content-Type": "application/json"}, - data=json.dumps(payload) + f"{SANDBOX_PROXY_URL}/api/inference", headers={"Content-Type": "application/json"}, data=json.dumps(payload) ) - + if response.status_code == 200: result = response.text.strip('"') print(f"[AGENT] inference(): Inference response: {len(result)} characters") @@ -37,27 +30,21 @@ def inference(model, temperature, messages): else: print(f"[AGENT] inference(): Inference failed with status {response.status_code}: {response.text}") return None - + except Exception as e: print(f"[AGENT] inference(): Inference request failed: {e}") return None - def embedding(input): try: - payload = { - "run_id": RUN_ID, - "input": input - } - - print(f"[AGENT] embedding(): Sending embedding request...") + payload = {"run_id": RUN_ID, "input": input} + + print("[AGENT] embedding(): Sending embedding request...") response = requests.post( - f"{SANDBOX_PROXY_URL}/api/embedding", - headers={"Content-Type": "application/json"}, - data=json.dumps(payload) + f"{SANDBOX_PROXY_URL}/api/embedding", headers={"Content-Type": "application/json"}, data=json.dumps(payload) ) - + if response.status_code == 200: result = response.json() print(f"[AGENT] embedding(): Embedding response: {len(result)} dimensions") @@ -65,36 +52,27 @@ def embedding(input): else: print(f"[AGENT] embedding(): Embedding failed with status {response.status_code}: {response.text}") return None - + except Exception as e: print(f"[AGENT] embedding(): Embedding request failed: {e}") return None - -def agent_main(input): +def agent_main(input): print("[AGENT] Entered agent_main()") - - # Test inference function message = "What is 2+2?" print(f"[AGENT] <-- '{message}'") - messages = [ - {"role": "user", "content": message} - ] + messages = [{"role": "user", "content": message}] inference_result = inference("moonshotai/Kimi-K2-Instruct", 0.5, messages) if inference_result: print(f"[AGENT] --> '{inference_result}'") - - print("[AGENT] Reading solution from /sandbox/solution.diff") with open("/sandbox/solution.diff", "r") as f: diff = f.read() - - print("[AGENT] Exiting agent_main()") - - return diff \ No newline at end of file + + return diff diff --git a/api/config.py b/api/config.py index 4fbd9a332..26ffc1ad7 100644 --- a/api/config.py +++ b/api/config.py @@ -1,15 +1,13 @@ import os -import utils.logger as logger from dotenv import load_dotenv - +import utils.logger as logger # Load everything from .env load_dotenv() - # Load host and port HOST = os.getenv("HOST") if not HOST: @@ -21,7 +19,6 @@ PORT = int(PORT) - # Load Bittensor configuration NETUID = os.getenv("NETUID") if not NETUID: @@ -37,7 +34,6 @@ logger.fatal("SUBTENSOR_NETWORK is not set in .env") - OWNER_HOTKEY = os.getenv("OWNER_HOTKEY") if not OWNER_HOTKEY: logger.fatal("OWNER_HOTKEY is not set in .env") @@ -62,7 +58,6 @@ logger.fatal("DISALLOW_UPLOADS_REASON is not set in .env") - # Load the environment configuration ENV = os.getenv("ENV") if not ENV: @@ -72,12 +67,11 @@ logger.fatal("ENV must be either 'prod' or 'dev'") - # Load AWS configuration AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID") if not AWS_ACCESS_KEY_ID: logger.fatal("AWS_ACCESS_KEY_ID is not set in .env") - + AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY") if not AWS_SECRET_ACCESS_KEY: logger.fatal("AWS_SECRET_ACCESS_KEY is not set in .env") @@ -87,38 +81,35 @@ logger.fatal("AWS_REGION is not set in .env") - # Load S3 configuration S3_BUCKET_NAME = os.getenv("S3_BUCKET_NAME") if not S3_BUCKET_NAME: logger.fatal("S3_BUCKET_NAME is not set in .env") - # Load database configuration DATABASE_USERNAME = os.getenv("DATABASE_USERNAME") if not DATABASE_USERNAME: logger.fatal("DATABASE_USERNAME is not set in .env") - + DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD") if not DATABASE_PASSWORD: logger.fatal("DATABASE_PASSWORD is not set in .env") - + DATABASE_HOST = os.getenv("DATABASE_HOST") if not DATABASE_HOST: logger.fatal("DATABASE_HOST is not set in .env") - + DATABASE_PORT = os.getenv("DATABASE_PORT") if not DATABASE_PORT: logger.fatal("DATABASE_PORT is not set in .env") DATABASE_PORT = int(DATABASE_PORT) - + DATABASE_NAME = os.getenv("DATABASE_NAME") if not DATABASE_NAME: logger.fatal("DATABASE_NAME is not set in .env") - # Load screener configuration SCREENER_PASSWORD = os.getenv("SCREENER_PASSWORD") if not SCREENER_PASSWORD: @@ -140,7 +131,6 @@ PRUNE_THRESHOLD = float(PRUNE_THRESHOLD) - # Load validator configuration VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS = os.getenv("VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS") if not VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS: @@ -153,7 +143,6 @@ VALIDATOR_HEARTBEAT_TIMEOUT_INTERVAL_SECONDS = int(VALIDATOR_HEARTBEAT_TIMEOUT_INTERVAL_SECONDS) - # Load validator configuration (sent to validator upon registration) VALIDATOR_RUNNING_AGENT_TIMEOUT_SECONDS = os.getenv("VALIDATOR_RUNNING_AGENT_TIMEOUT_SECONDS") if not VALIDATOR_RUNNING_AGENT_TIMEOUT_SECONDS: @@ -171,7 +160,6 @@ VALIDATOR_MAX_EVALUATION_RUN_LOG_SIZE_BYTES = int(VALIDATOR_MAX_EVALUATION_RUN_LOG_SIZE_BYTES) - MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS = os.getenv("MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS") if not MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS: logger.fatal("MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS is not set in .env") @@ -190,7 +178,9 @@ FETCH_METAGRAPH_INTERVAL_SECONDS = os.getenv("FETCH_METAGRAPH_INTERVAL_SECONDS") if not FETCH_METAGRAPH_INTERVAL_SECONDS: default_fetch_metagraph_interval_seconds = 120 - logger.warning(f"FETCH_METAGRAPH_INTERVAL_SECONDS is not set in .env, using default of {default_fetch_metagraph_interval_seconds} seconds") + logger.warning( + f"FETCH_METAGRAPH_INTERVAL_SECONDS is not set in .env, using default of {default_fetch_metagraph_interval_seconds} seconds" + ) FETCH_METAGRAPH_INTERVAL_SECONDS = default_fetch_metagraph_interval_seconds else: FETCH_METAGRAPH_INTERVAL_SECONDS = int(FETCH_METAGRAPH_INTERVAL_SECONDS) @@ -248,4 +238,4 @@ logger.info(f"Miner Agent Upload Rate Limit: {MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS} second(s)") logger.info(f"Number of Evaluations per Agent: {NUM_EVALS_PER_AGENT}") -logger.info("=========================") \ No newline at end of file +logger.info("=========================") diff --git a/api/endpoints/agent.py b/api/endpoints/agent.py index 3e5c304c1..d32bdc321 100644 --- a/api/endpoints/agent.py +++ b/api/endpoints/agent.py @@ -1,15 +1,13 @@ from uuid import UUID -from models.agent import Agent -from fastapi import APIRouter, HTTPException -from queries.agent import get_agent_by_evaluation_run_id - +from fastapi import APIRouter, HTTPException +from models.agent import Agent +from queries.agent import get_agent_by_evaluation_run_id router = APIRouter() - # /agent/get-by-evaluation-run-id?evaluation_run_id= @router.get("/get-by-evaluation-run-id") async def agent_get_by_evaluation_run_id(evaluation_run_id: UUID) -> Agent: @@ -18,4 +16,4 @@ async def agent_get_by_evaluation_run_id(evaluation_run_id: UUID) -> Agent: if agent is None: raise HTTPException(status_code=404, detail=f"Agent with evaluation run ID {evaluation_run_id} does not exist.") - return agent \ No newline at end of file + return agent diff --git a/api/endpoints/debug.py b/api/endpoints/debug.py index 4aa5f5005..e4fa1240b 100644 --- a/api/endpoints/debug.py +++ b/api/endpoints/debug.py @@ -1,21 +1,18 @@ from fastapi import APIRouter + from utils.database import get_debug_query_info from utils.debug_lock import get_debug_lock_info - - router = APIRouter() - # /debug/lock-info @router.get("/lock-info") async def debug_lock_info(): return get_debug_lock_info() - # /debug/query-info @router.get("/query-info") async def debug_query_info(): - return get_debug_query_info() \ No newline at end of file + return get_debug_query_info() diff --git a/api/endpoints/evaluation_run.py b/api/endpoints/evaluation_run.py index 2d1bc69d8..4e20a9d4a 100644 --- a/api/endpoints/evaluation_run.py +++ b/api/endpoints/evaluation_run.py @@ -1,25 +1,22 @@ from uuid import UUID -from fastapi import APIRouter, HTTPException -from queries.evaluation_run import get_evaluation_run_by_id -from models.evaluation_run import EvaluationRun, EvaluationRunLogType -from queries.evaluation_run import get_evaluation_run_logs_by_id +from fastapi import APIRouter, HTTPException +from models.evaluation_run import EvaluationRun, EvaluationRunLogType +from queries.evaluation_run import get_evaluation_run_by_id, get_evaluation_run_logs_by_id router = APIRouter() - # /evaluation-run/get-by-id?evaluation_run_id= @router.get("/get-by-id") async def evaluation_run_get_by_id(evaluation_run_id: UUID) -> EvaluationRun: evaluation_run = await get_evaluation_run_by_id(evaluation_run_id) - + if evaluation_run is None: raise HTTPException(status_code=404, detail=f"Evaluation run with ID {evaluation_run_id} does not exist.") - - return evaluation_run + return evaluation_run # /evaluation-run/get-logs-by-id?evaluation_run_id=&type= @@ -28,6 +25,8 @@ async def evaluation_run_get_logs_by_id(evaluation_run_id: UUID, type: Evaluatio logs = await get_evaluation_run_logs_by_id(evaluation_run_id, type) if logs is None: - raise HTTPException(status_code=404, detail=f"Evaluation run logs with ID {evaluation_run_id} and type {type} do not exist.") + raise HTTPException( + status_code=404, detail=f"Evaluation run logs with ID {evaluation_run_id} and type {type} do not exist." + ) - return logs \ No newline at end of file + return logs diff --git a/api/endpoints/evaluation_sets.py b/api/endpoints/evaluation_sets.py index c323d0641..dd9322c42 100644 --- a/api/endpoints/evaluation_sets.py +++ b/api/endpoints/evaluation_sets.py @@ -1,14 +1,13 @@ from typing import List -from fastapi import APIRouter -from models.evaluation_set import EvaluationSetProblem -from queries.evaluation_set import get_latest_set_id, get_all_evaluation_set_problems_for_set_id +from fastapi import APIRouter +from models.evaluation_set import EvaluationSetProblem +from queries.evaluation_set import get_all_evaluation_set_problems_for_set_id, get_latest_set_id router = APIRouter() - # /evaluation-sets/all-latest-set-problems @router.get("/all-latest-set-problems") async def evaluation_sets_all_latest_set_problems() -> List[EvaluationSetProblem]: diff --git a/api/endpoints/evaluations.py b/api/endpoints/evaluations.py index fc8570a9e..c8553d559 100644 --- a/api/endpoints/evaluations.py +++ b/api/endpoints/evaluations.py @@ -1,20 +1,21 @@ from uuid import UUID + from fastapi import APIRouter, HTTPException + from models.evaluation import HydratedEvaluation from queries.evaluation import get_hydrated_evaluation_by_evaluation_run_id - - router = APIRouter() - # /evaluations/get-by-evaluation-run-id?evaluation_run_id= @router.get("/get-by-evaluation-run-id") async def evaluations_get_by_evaluation_run_id(evaluation_run_id: UUID) -> HydratedEvaluation: evaluation = await get_hydrated_evaluation_by_evaluation_run_id(evaluation_run_id) - + if evaluation is None: - raise HTTPException(status_code=404, detail=f"Evaluation with evaluation run ID {evaluation_run_id} does not exist.") - - return evaluation \ No newline at end of file + raise HTTPException( + status_code=404, detail=f"Evaluation with evaluation run ID {evaluation_run_id} does not exist." + ) + + return evaluation diff --git a/api/endpoints/retrieval.py b/api/endpoints/retrieval.py index 2395bdd24..0cb104bc0 100644 --- a/api/endpoints/retrieval.py +++ b/api/endpoints/retrieval.py @@ -1,75 +1,84 @@ import asyncio - -from uuid import UUID -from pydantic import BaseModel -from utils.ttl import ttl_cache from typing import List, Optional +from uuid import UUID + from fastapi import APIRouter, HTTPException -from utils.s3 import download_text_file_from_s3 +from pydantic import BaseModel + +from models.agent import Agent, AgentScored, AgentStatus, BenchmarkAgentScored, PossiblyBenchmarkAgent +from models.evaluation import Evaluation, EvaluationWithRuns from models.evaluation_set import EvaluationSetGroup +from queries.agent import ( + get_agent_by_id, + get_agents_in_queue, + get_all_agents_by_miner_hotkey, + get_benchmark_agents, + get_latest_agent_for_miner_hotkey, + get_possibly_benchmark_agent_by_id, + get_top_agents, +) from queries.evaluation import get_evaluations_for_agent_id -from models.evaluation import Evaluation, EvaluationWithRuns from queries.evaluation_run import get_all_evaluation_runs_in_evaluation_id -from models.agent import Agent, AgentScored, AgentStatus, BenchmarkAgentScored, PossiblyBenchmarkAgent -from queries.agent import get_top_agents, get_agent_by_id, get_agents_in_queue, get_benchmark_agents, get_all_agents_by_miner_hotkey, get_latest_agent_for_miner_hotkey, get_possibly_benchmark_agent_by_id -from queries.statistics import top_score, TopScoreOverTime, agents_created_24_hrs, ProblemSetCreationTime, PerfectlySolvedOverTime, get_top_scores_over_time, score_improvement_24_hrs, get_perfectly_solved_over_time, get_problem_set_creation_times - - +from queries.statistics import ( + PerfectlySolvedOverTime, + ProblemSetCreationTime, + TopScoreOverTime, + agents_created_24_hrs, + get_perfectly_solved_over_time, + get_problem_set_creation_times, + get_top_scores_over_time, + score_improvement_24_hrs, + top_score, +) +from utils.s3 import download_text_file_from_s3 +from utils.ttl import ttl_cache router = APIRouter() - # /retrieval/queue?stage={screener_1|screener_2|validator} @router.get("/queue") -@ttl_cache(ttl_seconds=60) # 1 minute +@ttl_cache(ttl_seconds=60) # 1 minute async def queue(stage: EvaluationSetGroup) -> List[Agent]: return await get_agents_in_queue(stage) - # /retrieval/top-agents @router.get("/top-agents") -@ttl_cache(ttl_seconds=60) # 1 minute +@ttl_cache(ttl_seconds=60) # 1 minute async def top_agents() -> List[AgentScored]: return await get_top_agents(number_of_agents=50) - # /retrieval/benchmark-agents @router.get("/benchmark-agents") -@ttl_cache(ttl_seconds=10*60) # 10 minutes +@ttl_cache(ttl_seconds=10 * 60) # 10 minutes async def benchmark_agents() -> List[BenchmarkAgentScored]: return await get_benchmark_agents() - # /retrieval/agent-by-id?agent_id= @router.get("/agent-by-id") async def agent_by_id(agent_id: UUID) -> PossiblyBenchmarkAgent: agent = await get_possibly_benchmark_agent_by_id(agent_id) - + if agent is None: - raise HTTPException( - status_code=404, - detail=f"Agent with ID {agent_id} not found" - ) + raise HTTPException(status_code=404, detail=f"Agent with ID {agent_id} not found") return agent + # /retrieval/agent-by-hotkey?miner_hotkey= @router.get("/agent-by-hotkey") async def agent_by_hotkey(miner_hotkey: str) -> Agent: agent = await get_latest_agent_for_miner_hotkey(miner_hotkey=miner_hotkey) - + if agent is None: - raise HTTPException( - status_code=404, - detail=f"Agent with miner hotkey {miner_hotkey} not found" - ) + raise HTTPException(status_code=404, detail=f"Agent with miner hotkey {miner_hotkey} not found") return agent + # /retrieval/all-agents-by-hotkey?miner_hotkey= @router.get("/all-agents-by-hotkey") async def all_agents_by_hotkey(miner_hotkey: str) -> List[Agent]: @@ -77,79 +86,67 @@ async def all_agents_by_hotkey(miner_hotkey: str) -> List[Agent]: return agents - # TODO ADAM: optimize # /retrieval/evaluations-for-agent?agent_id= @router.get("/evaluations-for-agent") async def evaluations_for_agent(agent_id: UUID) -> List[EvaluationWithRuns]: evaluations: List[Evaluation] = await get_evaluations_for_agent_id(agent_id=agent_id) - + runs_per_eval = await asyncio.gather( *[get_all_evaluation_runs_in_evaluation_id(evaluation_id=e.evaluation_id) for e in evaluations] ) - return [ - EvaluationWithRuns(**e.model_dump(), runs=runs) - for e, runs in zip(evaluations, runs_per_eval) - ] - + return [EvaluationWithRuns(**e.model_dump(), runs=runs) for e, runs in zip(evaluations, runs_per_eval)] # /retrieval/agent-code?agent_id= @router.get("/agent-code") async def agent_code(agent_id: UUID) -> str: agent = await get_agent_by_id(agent_id=agent_id) - + if not agent: - raise HTTPException( - status_code=404, - detail=f"Agent with ID {agent_id} not found" - ) - + raise HTTPException(status_code=404, detail=f"Agent with ID {agent_id} not found") + if agent.status in [AgentStatus.screening_1, AgentStatus.screening_2, AgentStatus.evaluating]: - raise HTTPException( - status_code=403, - detail=f"Agent {agent.agent_id} is still being screened/evaluated" - ) - - return await download_text_file_from_s3(f"{agent_id}/agent.py") + raise HTTPException(status_code=403, detail=f"Agent {agent.agent_id} is still being screened/evaluated") + return await download_text_file_from_s3(f"{agent_id}/agent.py") # /retrieval/top-scores-over-time @router.get("/top-scores-over-time") -@ttl_cache(ttl_seconds=60 * 15) # 15 minutes +@ttl_cache(ttl_seconds=60 * 15) # 15 minutes async def top_scores_over_time() -> List[TopScoreOverTime]: return await get_top_scores_over_time() - # /retrieval/perfectly-solved-over-time class PerfectlySolvedOverTimeResponse(BaseModel): perfectly_solved_over_times: List[PerfectlySolvedOverTime] problem_set_creation_times: List[ProblemSetCreationTime] + @router.get("/perfectly-solved-over-time") -@ttl_cache(ttl_seconds=60 * 15) # 15 minutes +@ttl_cache(ttl_seconds=60 * 15) # 15 minutes async def perfectly_solved_over_time() -> PerfectlySolvedOverTimeResponse: return PerfectlySolvedOverTimeResponse( perfectly_solved_over_times=await get_perfectly_solved_over_time(), - problem_set_creation_times=await get_problem_set_creation_times() + problem_set_creation_times=await get_problem_set_creation_times(), ) - # /retrieval/network-statistics class NetworkStatisticsResponse(BaseModel): score_improvement_24_hrs: float agents_created_24_hrs: int top_score: Optional[float] + @router.get("/network-statistics") -@ttl_cache(ttl_seconds=60 * 15) # 15 minutes +@ttl_cache(ttl_seconds=60 * 15) # 15 minutes async def network_statistics() -> NetworkStatisticsResponse: return NetworkStatisticsResponse( score_improvement_24_hrs=await score_improvement_24_hrs(), agents_created_24_hrs=await agents_created_24_hrs(), - top_score=await top_score() + top_score=await top_score(), ) diff --git a/api/endpoints/scoring.py b/api/endpoints/scoring.py index dd6dcb053..1efdb9615 100644 --- a/api/endpoints/scoring.py +++ b/api/endpoints/scoring.py @@ -1,22 +1,23 @@ -import api.config as config +from datetime import datetime +from typing import Dict, Optional from fastapi import APIRouter -from datetime import datetime from pydantic import BaseModel -from utils.ttl import ttl_cache -from typing import Dict, Optional + +import api.config as config from models.evaluation_set import EvaluationSetGroup -from utils.bittensor import check_if_hotkey_is_registered -from queries.scores import get_weight_receiving_agent_hotkey from queries.evaluation_set import get_latest_set_id, get_set_created_at -from queries.statistics import get_average_score_per_evaluation_set_group, get_average_wait_time_per_evaluation_set_group - - +from queries.scores import get_weight_receiving_agent_hotkey +from queries.statistics import ( + get_average_score_per_evaluation_set_group, + get_average_wait_time_per_evaluation_set_group, +) +from utils.bittensor import check_if_hotkey_is_registered +from utils.ttl import ttl_cache router = APIRouter() - # /scoring/weights @router.get("/weights") async def weights() -> Dict[str, float]: @@ -37,7 +38,6 @@ async def weights() -> Dict[str, float]: return {config.OWNER_HOTKEY: 1.0} - # /scoring/screener-info class ScoringScreenerInfoResponse(BaseModel): screener_1_threshold: float @@ -52,8 +52,9 @@ class ScoringScreenerInfoResponse(BaseModel): screener_2_average_wait_time: Optional[float] = None validator_average_wait_time: Optional[float] = None + @router.get("/screener-info") -@ttl_cache(ttl_seconds=60) # 1 minute +@ttl_cache(ttl_seconds=60) # 1 minute async def screener_info() -> ScoringScreenerInfoResponse: average_score_per_evaluation_set_group = await get_average_score_per_evaluation_set_group() average_wait_time_per_evaluation_set_group = await get_average_wait_time_per_evaluation_set_group() @@ -62,18 +63,15 @@ async def screener_info() -> ScoringScreenerInfoResponse: screener_1_threshold=config.SCREENER_1_THRESHOLD, screener_2_threshold=config.SCREENER_2_THRESHOLD, prune_threshold=config.PRUNE_THRESHOLD, - screener_1_average_score=average_score_per_evaluation_set_group[EvaluationSetGroup.screener_1], screener_2_average_score=average_score_per_evaluation_set_group[EvaluationSetGroup.screener_2], validator_average_score=average_score_per_evaluation_set_group[EvaluationSetGroup.validator], - screener_1_average_wait_time=average_wait_time_per_evaluation_set_group[EvaluationSetGroup.screener_1], screener_2_average_wait_time=average_wait_time_per_evaluation_set_group[EvaluationSetGroup.screener_2], - validator_average_wait_time=average_wait_time_per_evaluation_set_group[EvaluationSetGroup.validator] + validator_average_wait_time=average_wait_time_per_evaluation_set_group[EvaluationSetGroup.validator], ) - # /scoring/latest-set-info class ScoringLatestSetInfo(BaseModel): latest_set_id: int @@ -84,7 +82,4 @@ class ScoringLatestSetInfo(BaseModel): async def latest_set_info() -> ScoringLatestSetInfo: latest_set_id = await get_latest_set_id() latest_set_created_at = await get_set_created_at(latest_set_id) - return ScoringLatestSetInfo( - latest_set_id=latest_set_id, - latest_set_created_at=latest_set_created_at - ) + return ScoringLatestSetInfo(latest_set_id=latest_set_id, latest_set_created_at=latest_set_created_at) diff --git a/api/endpoints/statistics.py b/api/endpoints/statistics.py index 99d0f0a24..319c7b078 100644 --- a/api/endpoints/statistics.py +++ b/api/endpoints/statistics.py @@ -1,14 +1,13 @@ import asyncio import datetime - -from pydantic import BaseModel -from utils.ttl import ttl_cache from typing import List, Optional + from fastapi import APIRouter, HTTPException +from pydantic import BaseModel + from queries.evaluation_set import get_latest_set_id, get_set_created_at from queries.problem_statistics import ProblemStatistics, get_problem_statistics - - +from utils.ttl import ttl_cache # NOTE ADAM: Set IDs 6 and earlier still included the validator optimization # of skipping all tests after the first failure, which means that @@ -18,36 +17,37 @@ EARLIEST_SET_ID_WITH_GOOD_DATA = 7 - router = APIRouter() - # /statistics/problem-statistics?set_id= class ProblemStatisticsResponse(BaseModel): problem_stats: List[ProblemStatistics] problem_set_id: int problem_set_created_at: datetime.datetime + @router.get("/problem-statistics") -@ttl_cache(ttl_seconds=15*60) # 15 mins +@ttl_cache(ttl_seconds=15 * 60) # 15 mins async def problem_statistics(set_id: Optional[int] = None) -> ProblemStatisticsResponse: max_problem_set_id = await get_latest_set_id() if set_id is None: - set_id = max_problem_set_id + set_id = max_problem_set_id elif set_id > max_problem_set_id: - raise HTTPException(status_code=400, detail=f"Set ID {set_id} is greater than the newest available set ID {max_problem_set_id}") + raise HTTPException( + status_code=400, detail=f"Set ID {set_id} is greater than the newest available set ID {max_problem_set_id}" + ) elif set_id < EARLIEST_SET_ID_WITH_GOOD_DATA: - raise HTTPException(status_code=400, detail=f"Set ID {set_id} is lesser than the oldest available set ID {EARLIEST_SET_ID_WITH_GOOD_DATA}") + raise HTTPException( + status_code=400, + detail=f"Set ID {set_id} is lesser than the oldest available set ID {EARLIEST_SET_ID_WITH_GOOD_DATA}", + ) problem_stats, problem_set_created_at = await asyncio.gather( - get_problem_statistics(set_id), - get_set_created_at(set_id) + get_problem_statistics(set_id), get_set_created_at(set_id) ) - + return ProblemStatisticsResponse( - problem_stats=problem_stats, - problem_set_id=set_id, - problem_set_created_at=problem_set_created_at + problem_stats=problem_stats, problem_set_id=set_id, problem_set_created_at=problem_set_created_at ) diff --git a/api/endpoints/validator.py b/api/endpoints/validator.py index 3c5b7f64c..497ef7622 100644 --- a/api/endpoints/validator.py +++ b/api/endpoints/validator.py @@ -1,35 +1,65 @@ import asyncio import re - import time from datetime import datetime, timedelta, timezone +from functools import wraps +from http import HTTPStatus from typing import Dict, List, Optional from uuid import UUID, uuid4 - -from utils.git import COMMIT_HASH -from utils.debug_lock import DebugLock -from http import HTTPStatus -from fastapi import Depends, APIRouter, HTTPException, Request +from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.security import HTTPBearer from pydantic import BaseModel -from functools import wraps + import api.config as config import utils.logger as logger -from queries.agent import get_top_agents, get_agent_by_id, update_agent_status, get_next_agent_id_awaiting_evaluation_for_validator_hotkey -from queries.evaluation import get_hydrated_evaluation_by_id, update_evaluation_finished_at, create_new_evaluation_and_evaluation_runs, get_num_successful_validator_evaluations_for_agent_id, update_unfinished_evaluation_runs_in_evaluation_id_to_errored -from queries.evaluation_run import get_evaluation_run_by_id, update_evaluation_run_by_id, \ - get_all_evaluation_runs_in_evaluation_id, create_evaluation_run_log, check_if_evaluation_run_logs_exist +from api.endpoints.validator_models import ( + ConnectedValidatorInfo, + ScreenerRegistrationRequest, + ScreenerRegistrationResponse, + ValidatorDisconnectRequest, + ValidatorDisconnectResponse, + ValidatorFinishEvaluationRequest, + ValidatorFinishEvaluationResponse, + ValidatorHeartbeatRequest, + ValidatorHeartbeatResponse, + ValidatorRegistrationRequest, + ValidatorRegistrationResponse, + ValidatorRequestEvaluationRequest, + ValidatorRequestEvaluationResponse, + ValidatorRequestEvaluationResponseEvaluationRun, + ValidatorUpdateEvaluationRunRequest, + ValidatorUpdateEvaluationRunResponse, +) from models.agent import Agent, AgentStatus from models.evaluation import Evaluation, EvaluationStatus -from models.evaluation_run import EvaluationRunStatus, EvaluationRunLogType -from models.problem import ProblemTestResult +from models.evaluation_run import EvaluationRunLogType, EvaluationRunStatus +from queries.agent import ( + get_agent_by_id, + get_next_agent_id_awaiting_evaluation_for_validator_hotkey, + get_top_agents, + update_agent_status, +) +from queries.evaluation import ( + create_new_evaluation_and_evaluation_runs, + get_hydrated_evaluation_by_id, + get_num_successful_validator_evaluations_for_agent_id, + update_evaluation_finished_at, + update_unfinished_evaluation_runs_in_evaluation_id_to_errored, +) +from queries.evaluation_run import ( + check_if_evaluation_run_logs_exist, + create_evaluation_run_log, + get_all_evaluation_runs_in_evaluation_id, + get_evaluation_run_by_id, + update_evaluation_run_by_id, +) from utils.bittensor import validate_signed_timestamp +from utils.debug_lock import DebugLock +from utils.git import COMMIT_HASH from utils.s3 import download_text_file_from_s3 from utils.system_metrics import SystemMetrics -from utils.validator_hotkeys import validator_hotkey_to_name, is_validator_hotkey_whitelisted -from api.endpoints.validator_models import * - +from utils.validator_hotkeys import is_validator_hotkey_whitelisted, validator_hotkey_to_name # A validator @@ -47,8 +77,6 @@ class Validator(BaseModel): time_last_heartbeat: Optional[datetime] = None system_metrics: Optional[SystemMetrics] = None - - _lock: asyncio.Lock @@ -57,10 +85,10 @@ def __init__(self, **data): self._lock = asyncio.Lock() - # Map of session IDs to validator objects SESSION_ID_TO_VALIDATOR: Dict[UUID, Validator] = {} + # Returns True if a validator with the given hotkey is currently registered def is_validator_registered(validator_hotkey: str) -> str | None: for session_id, validator in SESSION_ID_TO_VALIDATOR.items(): @@ -68,6 +96,7 @@ def is_validator_registered(validator_hotkey: str) -> str | None: return session_id return None + def delete_session_by_hotkey(validator_hotkey: str) -> Optional[UUID]: for session_id, validator in SESSION_ID_TO_VALIDATOR.items(): if validator.hotkey == validator_hotkey: @@ -75,12 +104,12 @@ def delete_session_by_hotkey(validator_hotkey: str) -> Optional[UUID]: return session_id return None + # Returns the IP addresses of all connected screeners and validators def get_all_connected_validator_ip_addresses() -> List[str]: return [validator.ip_address for validator in SESSION_ID_TO_VALIDATOR.values()] - # Deletes a validator from the SESSION_ID_TO_VALIDATOR map, and cleans up its associated state async def delete_validator(validator: Validator, reason: str) -> None: logger.info(f"Deleting validator {validator.name} ({validator.hotkey})...") @@ -95,6 +124,7 @@ async def delete_validator(validator: Validator, reason: str) -> None: logger.info(f"Deleted validator {validator.name} ({validator.hotkey})") + # Deletes all validators that have not sent a heartbeat in long enough async def delete_validators_that_have_not_sent_a_heartbeat() -> None: logger.info("Deleting validators that have not sent a heartbeat...") @@ -102,15 +132,21 @@ async def delete_validators_that_have_not_sent_a_heartbeat() -> None: _validators = list(SESSION_ID_TO_VALIDATOR.values()) for validator in _validators: time_last_heartbeat = validator.time_last_heartbeat or validator.time_connected - if time_last_heartbeat + timedelta(seconds=config.VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS) < datetime.now(timezone.utc): - async with DebugLock(validator._lock, f"delete_validators_that_have_not_sent_a_heartbeat() for {validator.name}'s lock"): + if time_last_heartbeat + timedelta(seconds=config.VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS) < datetime.now( + timezone.utc + ): + async with DebugLock( + validator._lock, f"delete_validators_that_have_not_sent_a_heartbeat() for {validator.name}'s lock" + ): if validator.session_id in SESSION_ID_TO_VALIDATOR: - await delete_validator(validator, f"The validator was disconnected because it did not send a heartbeat in {config.VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS} seconds.") + await delete_validator( + validator, + f"The validator was disconnected because it did not send a heartbeat in {config.VALIDATOR_HEARTBEAT_TIMEOUT_SECONDS} seconds.", + ) logger.info("Deleted validators that have not sent a heartbeat") - # Dependency to get the validator associated with the request # Requires that the request has a valid "Authorization: Bearer " header # See validator_request_evaluation() and other endpoints for usage examples @@ -119,33 +155,24 @@ async def get_request_validator(token: str = Depends(HTTPBearer())) -> Validator try: session_id = UUID(token.credentials) except ValueError: - raise HTTPException( - status_code=422, - detail="Invalid session ID format (expected a UUID)." - ) - + raise HTTPException(status_code=422, detail="Invalid session ID format (expected a UUID).") + # Make sure the session_id is associated with a validator if session_id not in SESSION_ID_TO_VALIDATOR: - raise HTTPException( - status_code=401, - detail="Session ID not found or expired." - ) - + raise HTTPException(status_code=401, detail="Session ID not found or expired.") + return SESSION_ID_TO_VALIDATOR[session_id] + # Exactly the same as get_request_validator, but locks the validator # The significance of this is that specific endpoints can use this dependency to prevent race conditions async def get_request_validator_with_lock(request: Request, validator: Validator = Depends(get_request_validator)): - async with DebugLock(validator._lock, f"{request.scope.get("endpoint").__name__}() for {validator.name}'s lock"): + async with DebugLock(validator._lock, f"{request.scope.get('endpoint').__name__}() for {validator.name}'s lock"): # Make sure the session_id is still associated with a validator if validator.session_id not in SESSION_ID_TO_VALIDATOR: - raise HTTPException( - status_code=401, - detail="Session ID not found or expired." - ) - - yield validator + raise HTTPException(status_code=401, detail="Session ID not found or expired.") + yield validator # Catches HTTP exceptions and cleans up the associated validator @@ -156,21 +183,22 @@ async def wrapper(*args, **kwargs): return await func(*args, **kwargs) except HTTPException as e: logger.error(f"Validator HTTP exception: {e.status_code} {e.detail}") - await delete_validator(kwargs['validator'], f"An HTTP exception was raised in {func.__name__}(): {e.status_code} {HTTPStatus(e.status_code).phrase}: {e.detail}") + await delete_validator( + kwargs["validator"], + f"An HTTP exception was raised in {func.__name__}(): {e.status_code} {HTTPStatus(e.status_code).phrase}: {e.detail}", + ) raise - return wrapper + return wrapper router = APIRouter() - # /validator/register-as-validator @router.post("/register-as-validator") async def validator_register_as_validator( - request: Request, - registration_request: ValidatorRegistrationRequest + request: Request, registration_request: ValidatorRegistrationRequest ) -> ValidatorRegistrationResponse: # Ensure that the commit hash matches @@ -178,38 +206,34 @@ async def validator_register_as_validator( raise HTTPException( status_code=426, detail=f"The provided validator commit hash ({registration_request.commit_hash}) does not match the platform commit hash ({COMMIT_HASH}). Run `git pull` to update your validator, and try again.", - headers={"X-Commit-Hash": COMMIT_HASH} + headers={"X-Commit-Hash": COMMIT_HASH}, ) # Ensure that the hotkey is in the list of acceptable validator hotkeys if not is_validator_hotkey_whitelisted(registration_request.hotkey): raise HTTPException( - status_code=403, - detail="The provided hotkey is not in the list of whitelisted validator hotkeys." + status_code=403, detail="The provided hotkey is not in the list of whitelisted validator hotkeys." ) - + # Check if the signed timestamp is valid (i.e., matches the raw timestamp) - if not validate_signed_timestamp(registration_request.timestamp, registration_request.signed_timestamp, registration_request.hotkey): + if not validate_signed_timestamp( + registration_request.timestamp, registration_request.signed_timestamp, registration_request.hotkey + ): raise HTTPException( - status_code=401, - detail="The provided signed timestamp does not match the provided timestamp." + status_code=401, detail="The provided signed timestamp does not match the provided timestamp." ) # Ensure that the timestamp is within 1 minute of the current time if abs(int(registration_request.timestamp) - int(time.time())) > 60: raise HTTPException( - status_code=400, - detail="The provided timestamp is not within 1 minute of the current time." + status_code=400, detail="The provided timestamp is not within 1 minute of the current time." ) # Ensure that the validator is not already registered ip_address = request.client.host if request.client else None if (old_session_id_ := is_validator_registered(registration_request.hotkey)) is not None: if ip_address != SESSION_ID_TO_VALIDATOR[old_session_id_].ip_address: - raise HTTPException( - status_code=409, - detail=f"There is already a validator connected with the given hotkey." - ) + raise HTTPException(status_code=409, detail="There is already a validator connected with the given hotkey.") else: # delete old session old_session_id = delete_session_by_hotkey(registration_request.hotkey) @@ -223,27 +247,27 @@ async def validator_register_as_validator( name=validator_hotkey_to_name(registration_request.hotkey), hotkey=registration_request.hotkey, time_connected=datetime.now(timezone.utc), - ip_address=ip_address + ip_address=ip_address, + ) + + logger.info( + f"Validator '{validator_hotkey_to_name(registration_request.hotkey)}' ({registration_request.hotkey}) was registered" ) - - logger.info(f"Validator '{validator_hotkey_to_name(registration_request.hotkey)}' ({registration_request.hotkey}) was registered") logger.info(f" Session ID: {session_id}") logger.info(f" IP Address: {ip_address}") - + return ValidatorRegistrationResponse( session_id=session_id, running_agent_timeout_seconds=config.VALIDATOR_RUNNING_AGENT_TIMEOUT_SECONDS, running_eval_timeout_seconds=config.VALIDATOR_RUNNING_EVAL_TIMEOUT_SECONDS, - max_evaluation_run_log_size_bytes=config.VALIDATOR_MAX_EVALUATION_RUN_LOG_SIZE_BYTES + max_evaluation_run_log_size_bytes=config.VALIDATOR_MAX_EVALUATION_RUN_LOG_SIZE_BYTES, ) - # /validator/register-as-screener @router.post("/register-as-screener") async def validator_register_as_screener( - request: Request, - registration_request: ScreenerRegistrationRequest + request: Request, registration_request: ScreenerRegistrationRequest ) -> ScreenerRegistrationResponse: # Ensure that the commit hash matches @@ -251,37 +275,25 @@ async def validator_register_as_screener( raise HTTPException( status_code=426, detail=f"The provided screener commit hash ({registration_request.commit_hash}) does not match the platform commit hash ({COMMIT_HASH}). Run `git pull` to update your screener, and try again.", - headers={"X-Commit-Hash": COMMIT_HASH} + headers={"X-Commit-Hash": COMMIT_HASH}, ) # Ensure that the name is in the format screener-CLASS-NUM if not re.match(r"screener-\d-\d+", registration_request.name): - raise HTTPException( - status_code=400, - detail="The provided name is not in the format screener-CLASS-NUM." - ) - + raise HTTPException(status_code=400, detail="The provided name is not in the format screener-CLASS-NUM.") + # Ensure that the CLASS is either 1 or 2 screener_class = registration_request.name.split("-")[1] if screener_class != "1" and screener_class != "2": - raise HTTPException( - status_code=400, - detail="The screener class must be either 1 or 2." - ) - + raise HTTPException(status_code=400, detail="The screener class must be either 1 or 2.") + # Ensure that the password is correct if registration_request.password != config.SCREENER_PASSWORD: - raise HTTPException( - status_code=403, - detail="The provided password is incorrect." - ) + raise HTTPException(status_code=403, detail="The provided password is incorrect.") # Ensure that the screener is not already registered if is_validator_registered(registration_request.name): - raise HTTPException( - status_code=409, - detail=f"There is already a screener connected with the given name." - ) + raise HTTPException(status_code=409, detail="There is already a screener connected with the given name.") # Register the screener with a new session ID session_id = uuid4() @@ -291,39 +303,38 @@ async def validator_register_as_screener( name=registration_request.name, hotkey=registration_request.name, time_connected=datetime.now(timezone.utc), - ip_address=ip_address + ip_address=ip_address, ) - + logger.info(f"Screener {registration_request.name} was registered") logger.info(f" Session ID: {session_id}") logger.info(f" IP Address: {ip_address}") - + return ScreenerRegistrationResponse( session_id=session_id, running_agent_timeout_seconds=config.VALIDATOR_RUNNING_AGENT_TIMEOUT_SECONDS, running_eval_timeout_seconds=config.VALIDATOR_RUNNING_EVAL_TIMEOUT_SECONDS, - max_evaluation_run_log_size_bytes=config.VALIDATOR_MAX_EVALUATION_RUN_LOG_SIZE_BYTES + max_evaluation_run_log_size_bytes=config.VALIDATOR_MAX_EVALUATION_RUN_LOG_SIZE_BYTES, ) - # /validator/request-evaluation validator_request_evaluation_lock = asyncio.Lock() screener_1_request_evaluation_lock = asyncio.Lock() screener_2_request_evaluation_lock = asyncio.Lock() + @router.post("/request-evaluation") @handle_validator_http_exceptions async def validator_request_evaluation( - request: ValidatorRequestEvaluationRequest, - validator: Validator = Depends(get_request_validator_with_lock) + request: ValidatorRequestEvaluationRequest, validator: Validator = Depends(get_request_validator_with_lock) ) -> Optional[ValidatorRequestEvaluationResponse]: # Make sure the validator is not already running an evaluation if validator.current_evaluation_id is not None: raise HTTPException( status_code=409, - detail=f"This validator is already running an evaluation, and validators may only run one evaluation at a time." + detail="This validator is already running an evaluation, and validators may only run one evaluation at a time.", ) # Record a heartbeat for the validator @@ -361,18 +372,22 @@ async def validator_request_evaluation( validator.current_evaluation = evaluation validator.current_agent = await get_agent_by_id(agent_id) - - logger.info(f"Validator '{validator.name}' requested an evaluation") logger.info(f" Agent ID: {agent_id}") logger.info(f" Evaluation ID: {evaluation.evaluation_id}") logger.info(f" # of Evaluation Runs: {len(evaluation_runs)}") agent_code = await download_text_file_from_s3(f"{agent_id}/agent.py") - evaluation_runs = [ValidatorRequestEvaluationResponseEvaluationRun(evaluation_run_id=evaluation_run.evaluation_run_id, problem_name=evaluation_run.problem_name) for evaluation_run in evaluation_runs] + evaluation_runs = [ + ValidatorRequestEvaluationResponseEvaluationRun( + evaluation_run_id=evaluation_run.evaluation_run_id, problem_name=evaluation_run.problem_name + ) + for evaluation_run in evaluation_runs + ] return ValidatorRequestEvaluationResponse(agent_code=agent_code, evaluation_runs=evaluation_runs) + def record_validator_heartbeat(validator: Validator, system_metrics: SystemMetrics | None = None) -> None: validator.time_last_heartbeat = datetime.now(timezone.utc) if system_metrics is not None: @@ -383,31 +398,29 @@ def record_validator_heartbeat(validator: Validator, system_metrics: SystemMetri @router.post("/heartbeat") async def validator_heartbeat( request: ValidatorHeartbeatRequest, - validator: Validator = Depends(get_request_validator) # No lock required + validator: Validator = Depends(get_request_validator), # No lock required ) -> ValidatorHeartbeatResponse: # logger.info(f"Validator '{validator.name}' sent a heartbeat") # logger.info(f" System metrics: {request.system_metrics}") record_validator_heartbeat(validator, request.system_metrics) - - return ValidatorHeartbeatResponse() + return ValidatorHeartbeatResponse() # /validator/update-evaluation-run @router.post("/update-evaluation-run") @handle_validator_http_exceptions async def validator_update_evaluation_run( - request: ValidatorUpdateEvaluationRunRequest, - validator: Validator = Depends(get_request_validator_with_lock) + request: ValidatorUpdateEvaluationRunRequest, validator: Validator = Depends(get_request_validator_with_lock) ) -> ValidatorUpdateEvaluationRunResponse: - + # Make sure the validator is currently running an evaluation if validator.current_evaluation_id is None: raise HTTPException( status_code=409, - detail=f"This validator is not currently running an evaluation, and therefore cannot update an evaluation run." + detail="This validator is not currently running an evaluation, and therefore cannot update an evaluation run.", ) # Record a heartbeat for the validator @@ -419,78 +432,66 @@ async def validator_update_evaluation_run( # Make sure that the evaluation run actually exists if evaluation_run is None: raise HTTPException( - status_code=404, - detail=f"Evaluation run with ID {request.evaluation_run_id} does not exist." + status_code=404, detail=f"Evaluation run with ID {request.evaluation_run_id} does not exist." ) # Make sure that the evaluation run is associated with the validator's current evaluation if evaluation_run.evaluation_id != validator.current_evaluation_id: raise HTTPException( status_code=403, - detail=f"The evaluation run with ID {request.evaluation_run_id} is not associated with the validator's current evaluation." + detail=f"The evaluation run with ID {request.evaluation_run_id} is not associated with the validator's current evaluation.", ) - - # The logic differs based on the updated status of the evaluation run match request.updated_status: case EvaluationRunStatus.pending: # There is no case where the validator should update an evaluation run to pending, since all evaluation runs start as pending - raise HTTPException( - status_code=400, - detail="An evaluation run can never be updated to pending." - ) - - + raise HTTPException(status_code=400, detail="An evaluation run can never be updated to pending.") case EvaluationRunStatus.initializing_agent: # A validator may only update an evaluation run to initializing_agent if the evaluation run is currently in the pending status if evaluation_run.status != EvaluationRunStatus.pending: raise HTTPException( status_code=400, - detail=f"An evaluation run can only be updated to initializing_agent if it is currently in the pending status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}." + detail=f"An evaluation run can only be updated to initializing_agent if it is currently in the pending status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}.", ) # Update the evaluation run to initializing_agent evaluation_run.status = EvaluationRunStatus.initializing_agent evaluation_run.started_initializing_agent_at = datetime.now(timezone.utc) - - case EvaluationRunStatus.running_agent: # A validator may only update an evaluation run to running_agent if the evaluation run is currently in the initializing_agent status if evaluation_run.status != EvaluationRunStatus.initializing_agent: raise HTTPException( status_code=400, - detail=f"An evaluation run can only be updated to running_agent if it is currently in the initializing_agent status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}." + detail=f"An evaluation run can only be updated to running_agent if it is currently in the initializing_agent status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}.", ) # Update the evaluation run to running_agent evaluation_run.status = EvaluationRunStatus.running_agent evaluation_run.started_running_agent_at = datetime.now(timezone.utc) - - case EvaluationRunStatus.initializing_eval: # A validator may only update an evaluation run to initializing_eval if the evaluation run is currently in the running_agent status if evaluation_run.status != EvaluationRunStatus.running_agent: raise HTTPException( status_code=400, - detail=f"An evaluation run can only be updated to initializing_eval if it is currently in the running_agent status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}." + detail=f"An evaluation run can only be updated to initializing_eval if it is currently in the running_agent status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}.", ) # Make sure that the patch is provided if request.patch is None: raise HTTPException( status_code=422, - detail="The patch is required when updating an evaluation run to initializing_eval." + detail="The patch is required when updating an evaluation run to initializing_eval.", ) - + # Make sure that the agent logs are provided if request.agent_logs is None: raise HTTPException( status_code=422, - detail="The agent logs are required when updating an evaluation run to initializing_eval." + detail="The agent logs are required when updating an evaluation run to initializing_eval.", ) # Update the evaluation run to initializing_eval @@ -499,44 +500,40 @@ async def validator_update_evaluation_run( evaluation_run.started_initializing_eval_at = datetime.now(timezone.utc) # Create an evaluation run log - await create_evaluation_run_log(evaluation_run.evaluation_run_id, EvaluationRunLogType.agent, request.agent_logs) - - + await create_evaluation_run_log( + evaluation_run.evaluation_run_id, EvaluationRunLogType.agent, request.agent_logs + ) case EvaluationRunStatus.running_eval: # A validator may only update an evaluation run to running_eval if the evaluation run is currently in the initializing_eval status if evaluation_run.status != EvaluationRunStatus.initializing_eval: raise HTTPException( status_code=400, - detail=f"An evaluation run can only be updated to running_eval if it is currently in the initializing_eval status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}." + detail=f"An evaluation run can only be updated to running_eval if it is currently in the initializing_eval status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}.", ) # Update the evaluation run to running_eval evaluation_run.status = EvaluationRunStatus.running_eval evaluation_run.started_running_eval_at = datetime.now(timezone.utc) - - case EvaluationRunStatus.finished: - # A validator may only update an evaluation run to finished if the evaluation run is currently in the running_eval status + # A validator may only update an evaluation run to finished if the evaluation run is currently in the running_eval status if evaluation_run.status != EvaluationRunStatus.running_eval: raise HTTPException( status_code=400, - detail=f"An evaluation run can only be updated to finished if it is currently in the running_eval status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}." + detail=f"An evaluation run can only be updated to finished if it is currently in the running_eval status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}.", ) # Make sure the test results are provided if request.test_results is None: raise HTTPException( - status_code=422, - detail="The test results are required when updating an evaluation run to finished." + status_code=422, detail="The test results are required when updating an evaluation run to finished." ) # Make sure the eval logs are provided if request.eval_logs is None: raise HTTPException( - status_code=422, - detail="The eval logs are required when updating an evaluation run to finished." + status_code=422, detail="The eval logs are required when updating an evaluation run to finished." ) # Update the evaluation run to finished @@ -545,9 +542,9 @@ async def validator_update_evaluation_run( evaluation_run.finished_or_errored_at = datetime.now(timezone.utc) # Create an evaluation run log - await create_evaluation_run_log(evaluation_run.evaluation_run_id, EvaluationRunLogType.eval, request.eval_logs) - - + await create_evaluation_run_log( + evaluation_run.evaluation_run_id, EvaluationRunLogType.eval, request.eval_logs + ) case EvaluationRunStatus.error: # A validator may only update an evaluation run to error if the evaluation run is currently in the pending, initializing_agent, running_agent, initializing_eval, or running_eval status @@ -556,40 +553,38 @@ async def validator_update_evaluation_run( EvaluationRunStatus.initializing_agent, EvaluationRunStatus.running_agent, EvaluationRunStatus.initializing_eval, - EvaluationRunStatus.running_eval + EvaluationRunStatus.running_eval, ]: raise HTTPException( status_code=400, - detail=f"An evaluation run can only be updated to error if it is currently in the pending, initializing_agent, running_agent, initializing_eval, or running_eval status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}." + detail=f"An evaluation run can only be updated to error if it is currently in the pending, initializing_agent, running_agent, initializing_eval, or running_eval status. The current status of evaluation run {request.evaluation_run_id} is {evaluation_run.status}.", ) # Make sure the error code is provided if request.error_code is None: raise HTTPException( - status_code=422, - detail="The error code is required when updating an evaluation run to error." + status_code=422, detail="The error code is required when updating an evaluation run to error." ) - + # Make sure the error message is provided if request.error_message is None: raise HTTPException( - status_code=422, - detail="The error message is required when updating an evaluation run to error." + status_code=422, detail="The error message is required when updating an evaluation run to error." ) # Agent logs can only be provided if none already exist - if request.agent_logs is not None and await check_if_evaluation_run_logs_exist(evaluation_run.evaluation_run_id, EvaluationRunLogType.agent): + if request.agent_logs is not None and await check_if_evaluation_run_logs_exist( + evaluation_run.evaluation_run_id, EvaluationRunLogType.agent + ): raise HTTPException( - status_code=422, - detail="The agent logs can only be provided if none already exist." + status_code=422, detail="The agent logs can only be provided if none already exist." ) # Evaluation logs can only be provided if none already exist - if request.eval_logs is not None and await check_if_evaluation_run_logs_exist(evaluation_run.evaluation_run_id, EvaluationRunLogType.eval): - raise HTTPException( - status_code=422, - detail="The eval logs can only be provided if none already exist." - ) + if request.eval_logs is not None and await check_if_evaluation_run_logs_exist( + evaluation_run.evaluation_run_id, EvaluationRunLogType.eval + ): + raise HTTPException(status_code=422, detail="The eval logs can only be provided if none already exist.") # Update the evaluation run to error evaluation_run.status = EvaluationRunStatus.error @@ -599,11 +594,13 @@ async def validator_update_evaluation_run( # Create evaluation run logs if request.agent_logs is not None: - await create_evaluation_run_log(evaluation_run.evaluation_run_id, EvaluationRunLogType.agent, request.agent_logs) + await create_evaluation_run_log( + evaluation_run.evaluation_run_id, EvaluationRunLogType.agent, request.agent_logs + ) if request.eval_logs is not None: - await create_evaluation_run_log(evaluation_run.evaluation_run_id, EvaluationRunLogType.eval, request.eval_logs) - - + await create_evaluation_run_log( + evaluation_run.evaluation_run_id, EvaluationRunLogType.eval, request.eval_logs + ) await update_evaluation_run_by_id(evaluation_run) @@ -614,12 +611,10 @@ async def validator_update_evaluation_run( return ValidatorUpdateEvaluationRunResponse() - # /validator/disconnect @router.post("/disconnect") async def validator_disconnect( - request: ValidatorDisconnectRequest, - validator: Validator = Depends(get_request_validator_with_lock) + request: ValidatorDisconnectRequest, validator: Validator = Depends(get_request_validator_with_lock) ) -> ValidatorDisconnectResponse: await delete_validator(validator, f"The validator disconnected. Reason: {request.reason}") @@ -630,20 +625,18 @@ async def validator_disconnect( return ValidatorDisconnectResponse() - # /validator/finish-evaluation @router.post("/finish-evaluation") @handle_validator_http_exceptions async def validator_finish_evaluation( - request: ValidatorFinishEvaluationRequest, - validator: Validator = Depends(get_request_validator_with_lock) + request: ValidatorFinishEvaluationRequest, validator: Validator = Depends(get_request_validator_with_lock) ) -> ValidatorFinishEvaluationResponse: # Make sure the validator is currently running an evaluation if validator.current_evaluation_id is None: raise HTTPException( status_code=409, - detail="This validator is not currently running an evaluation, and therefore cannot request to finish an evaluation." + detail="This validator is not currently running an evaluation, and therefore cannot request to finish an evaluation.", ) # Record a heartbeat for the validator @@ -651,14 +644,15 @@ async def validator_finish_evaluation( # Make sure that all evaluation runs have either finished or errored evaluation_runs = await get_all_evaluation_runs_in_evaluation_id(validator.current_evaluation_id) - if any(evaluation_run.status not in [EvaluationRunStatus.finished, EvaluationRunStatus.error] for evaluation_run in evaluation_runs): + if any( + evaluation_run.status not in [EvaluationRunStatus.finished, EvaluationRunStatus.error] + for evaluation_run in evaluation_runs + ): raise HTTPException( status_code=409, - detail="Not all evaluation runs associated with the evaluation that this validator is currently running have either finished or errored. Did you forget to send an update-evaluation-run?" + detail="Not all evaluation runs associated with the evaluation that this validator is currently running have either finished or errored. Did you forget to send an update-evaluation-run?", ) - - await handle_evaluation_if_finished(validator.current_evaluation_id) logger.info(f"Validator '{validator.name}' finished an evaluation") @@ -669,13 +663,6 @@ async def validator_finish_evaluation( return ValidatorFinishEvaluationResponse() - - - - - - - # /validator/connected-validators-info @router.get("/connected-validators-info") async def validator_connected_validators_info() -> List[ConnectedValidatorInfo]: @@ -688,7 +675,7 @@ async def validator_connected_validators_info() -> List[ConnectedValidatorInfo]: hotkey=validator.hotkey, time_connected=validator.time_connected, time_last_heartbeat=validator.time_last_heartbeat, - system_metrics=validator.system_metrics + system_metrics=validator.system_metrics, ) if validator.current_evaluation_id is not None: @@ -700,10 +687,6 @@ async def validator_connected_validators_info() -> List[ConnectedValidatorInfo]: return connected_validators - - - - async def handle_evaluation_if_finished(evaluation_id: UUID) -> None: """ Adds the finished_at field to an evaluation. If the evaluation is marked as successful @@ -747,4 +730,4 @@ async def handle_evaluation_if_finished(evaluation_id: UUID) -> None: # raise ValueError(f"Invalid agent status: {agent.status}, this should never happen") return - await update_agent_status(hydrated_evaluation.agent_id, new_agent_status) \ No newline at end of file + await update_agent_status(hydrated_evaluation.agent_id, new_agent_status) diff --git a/api/endpoints/validator_models.py b/api/endpoints/validator_models.py index 2ddda50bb..4a00b9e05 100644 --- a/api/endpoints/validator_models.py +++ b/api/endpoints/validator_models.py @@ -1,13 +1,14 @@ -from uuid import UUID -from models.agent import Agent from datetime import datetime -from pydantic import BaseModel from typing import List, Optional +from uuid import UUID + +from pydantic import BaseModel + +from models.agent import Agent from models.evaluation import Evaluation +from models.evaluation_run import EvaluationRunStatus from models.problem import ProblemTestResult from utils.system_metrics import SystemMetrics -from models.evaluation_run import EvaluationRunStatus - class ValidatorRegistrationRequest(BaseModel): @@ -16,6 +17,7 @@ class ValidatorRegistrationRequest(BaseModel): hotkey: str commit_hash: str + class ValidatorRegistrationResponse(BaseModel): session_id: UUID running_agent_timeout_seconds: int @@ -23,12 +25,12 @@ class ValidatorRegistrationResponse(BaseModel): max_evaluation_run_log_size_bytes: int - class ScreenerRegistrationRequest(BaseModel): name: str password: str commit_hash: str + class ScreenerRegistrationResponse(BaseModel): session_id: UUID running_agent_timeout_seconds: int @@ -36,32 +38,32 @@ class ScreenerRegistrationResponse(BaseModel): max_evaluation_run_log_size_bytes: int - class ValidatorRequestEvaluationRequest(BaseModel): pass -class ValidatorRequestEvaluationResponseEvaluationRun(BaseModel): # :( + +class ValidatorRequestEvaluationResponseEvaluationRun(BaseModel): # :( evaluation_run_id: UUID problem_name: str + class ValidatorRequestEvaluationResponse(BaseModel): agent_code: str evaluation_runs: List[ValidatorRequestEvaluationResponseEvaluationRun] - class ValidatorHeartbeatRequest(BaseModel): system_metrics: SystemMetrics + class ValidatorHeartbeatResponse(BaseModel): pass - class ValidatorUpdateEvaluationRunRequest(BaseModel): evaluation_run_id: UUID updated_status: EvaluationRunStatus - + patch: Optional[str] = None test_results: Optional[List[ProblemTestResult]] = None @@ -71,27 +73,27 @@ class ValidatorUpdateEvaluationRunRequest(BaseModel): error_code: Optional[int] = None error_message: Optional[str] = None + class ValidatorUpdateEvaluationRunResponse(BaseModel): pass - class ValidatorDisconnectRequest(BaseModel): reason: str + class ValidatorDisconnectResponse(BaseModel): pass - class ValidatorFinishEvaluationRequest(BaseModel): pass + class ValidatorFinishEvaluationResponse(BaseModel): pass - class ConnectedValidatorInfo(BaseModel): name: str hotkey: str @@ -101,4 +103,4 @@ class ConnectedValidatorInfo(BaseModel): system_metrics: Optional[SystemMetrics] = None evaluation: Optional[Evaluation] = None - agent: Optional[Agent] = None \ No newline at end of file + agent: Optional[Agent] = None diff --git a/api/loops/fetch_metagraph.py b/api/loops/fetch_metagraph.py index e6fcbbb5f..17b17acf4 100644 --- a/api/loops/fetch_metagraph.py +++ b/api/loops/fetch_metagraph.py @@ -1,15 +1,14 @@ import asyncio + import api.config as config import utils.logger as logger - from utils.bittensor import fetch_and_save_registered_hotkeys - async def fetch_metagraph_loop(): logger.info("Starting fetch metagraph loop...") while True: await fetch_and_save_registered_hotkeys() - await asyncio.sleep(config.FETCH_METAGRAPH_INTERVAL_SECONDS) \ No newline at end of file + await asyncio.sleep(config.FETCH_METAGRAPH_INTERVAL_SECONDS) diff --git a/api/loops/validator_heartbeat_timeout.py b/api/loops/validator_heartbeat_timeout.py index 51d134d9e..8322a4ec6 100644 --- a/api/loops/validator_heartbeat_timeout.py +++ b/api/loops/validator_heartbeat_timeout.py @@ -1,15 +1,14 @@ import asyncio + import api.config as config import utils.logger as logger - from api.endpoints.validator import delete_validators_that_have_not_sent_a_heartbeat - async def validator_heartbeat_timeout_loop(): logger.info("Starting validator heartbeat timeout loop...") while True: await delete_validators_that_have_not_sent_a_heartbeat() - await asyncio.sleep(config.VALIDATOR_HEARTBEAT_TIMEOUT_INTERVAL_SECONDS) \ No newline at end of file + await asyncio.sleep(config.VALIDATOR_HEARTBEAT_TIMEOUT_INTERVAL_SECONDS) diff --git a/api/src/endpoints/upload.py b/api/src/endpoints/upload.py index a86546782..b02f2a2e5 100644 --- a/api/src/endpoints/upload.py +++ b/api/src/endpoints/upload.py @@ -1,28 +1,35 @@ -from bittensor import Subtensor - import asyncio import os -import pprint import uuid from datetime import datetime, timezone from typing import Optional -from fastapi import APIRouter, Depends, UploadFile, File, Form, HTTPException, Request -from numpy import meshgrid +from bittensor import Subtensor +from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile from pydantic import BaseModel, Field -from api.src.utils.request_cache import hourly_cache -from queries.payments import retrieve_payment_by_hash, record_evaluation_payment -from utils.debug_lock import DebugLock import utils.logger as logger -from queries.agent import create_agent, record_upload_attempt -from queries.agent import get_latest_agent_for_miner_hotkey, get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id +from api import config +from api.src.utils.request_cache import hourly_cache +from api.src.utils.upload_agent_helpers import ( + check_agent_banned, + check_hotkey_registered, + check_if_python_file, + check_rate_limit, + check_signature, + get_miner_hotkey, +) +from models.agent import Agent, AgentStatus +from queries.agent import ( + create_agent, + get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id, + get_latest_agent_for_miner_hotkey, + record_upload_attempt, +) from queries.banned_hotkey import get_banned_hotkey -from api.src.utils.upload_agent_helpers import get_miner_hotkey, check_if_python_file, check_agent_banned, \ - check_rate_limit, check_signature, check_hotkey_registered, check_file_size -from models.agent import AgentStatus, Agent +from queries.payments import record_evaluation_payment, retrieve_payment_by_hash from utils.coingecko import get_tao_price -from api import config +from utils.debug_lock import DebugLock # TODO STEPHEN: we should have a global singleton subtensor = Subtensor(network=config.SUBTENSOR_NETWORK) @@ -30,12 +37,15 @@ # We use a lock per hotkey to prevent multiple agents being uploaded at the same time for the same hotkey hotkey_locks: dict[str, asyncio.Lock] = {} hotkey_locks_lock = asyncio.Lock() + + async def get_hotkey_lock(hotkey: str) -> asyncio.Lock: async with hotkey_locks_lock: if hotkey not in hotkey_locks: hotkey_locks[hotkey] = asyncio.Lock() return hotkey_locks[hotkey] + prod = False if os.getenv("ENV") == "prod": logger.info("Agent Upload running in production mode.") @@ -43,43 +53,46 @@ async def get_hotkey_lock(hotkey: str) -> asyncio.Lock: else: logger.info("Agent Upload running in development mode.") + class AgentUploadResponse(BaseModel): """Response model for successful agent upload""" + status: str = Field(..., description="Status of the upload operation") message: str = Field(..., description="Detailed message about the upload result") + class ErrorResponse(BaseModel): """Error response model""" + detail: str = Field(..., description="Error message describing what went wrong") + router = APIRouter() -@router.post( - "/agent/check", - tags=["upload"], - response_model=AgentUploadResponse -) + +@router.post("/agent/check", tags=["upload"], response_model=AgentUploadResponse) async def check_agent_post( request: Request, agent_file: UploadFile = File(..., description="Python file containing the agent code (must be named agent.py)"), public_key: str = Form(..., description="Public key of the miner in hex format"), - file_info: str = Form(..., description="File information containing miner hotkey and version number (format: hotkey:version)"), + file_info: str = Form( + ..., description="File information containing miner hotkey and version number (format: hotkey:version)" + ), signature: str = Form(..., description="Signature to verify the authenticity of the upload"), name: str = Form(..., description="Name of the agent"), payment_time: float = Form(..., description="Timestamp of the payment"), ) -> AgentUploadResponse: if config.DISALLOW_UPLOADS: - raise HTTPException( - status_code=503, - detail=config.DISALLOW_UPLOADS_REASON - ) + raise HTTPException(status_code=503, detail=config.DISALLOW_UPLOADS_REASON) miner_hotkey = get_miner_hotkey(file_info) - latest_agent_created_at_in_latest_set_id = await get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id(miner_hotkey=miner_hotkey) + latest_agent_created_at_in_latest_set_id = await get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id( + miner_hotkey=miner_hotkey + ) if latest_agent_created_at_in_latest_set_id: check_rate_limit(latest_agent_created_at_in_latest_set_id) check_signature(public_key, file_info, signature) await check_hotkey_registered(miner_hotkey) - await check_agent_banned(miner_hotkey=miner_hotkey) + await check_agent_banned(miner_hotkey=miner_hotkey) check_if_python_file(agent_file.filename) coldkey = subtensor.get_hotkey_owner(hotkey_ss58=miner_hotkey) miner_balance = subtensor.get_balance(address=coldkey).rao @@ -87,12 +100,10 @@ async def check_agent_post( if payment_cost.amount_rao > miner_balance: raise HTTPException( status_code=402, - detail=f"Insufficient balance. You need {payment_cost.amount_rao} RAO to upload this agent. You have {miner_balance} RAO." + detail=f"Insufficient balance. You need {payment_cost.amount_rao} RAO to upload this agent. You have {miner_balance} RAO.", ) - return AgentUploadResponse( - status="success", - message=f"Agent check successful" - ) + return AgentUploadResponse(status="success", message="Agent check successful") + @router.post( "/agent", @@ -104,14 +115,16 @@ async def check_agent_post( 409: {"model": ErrorResponse, "description": "Conflict - Upload request already processed"}, 429: {"model": ErrorResponse, "description": "Too Many Requests - Rate limit exceeded"}, 500: {"model": ErrorResponse, "description": "Internal Server Error - Server-side processing failed"}, - 503: {"model": ErrorResponse, "description": "Service Unavailable - No screeners available for evaluation"} - } + 503: {"model": ErrorResponse, "description": "Service Unavailable - No screeners available for evaluation"}, + }, ) async def post_agent( request: Request, agent_file: UploadFile = File(..., description="Python file containing the agent code (must be named agent.py)"), public_key: str = Form(..., description="Public key of the miner in hex format"), - file_info: str = Form(..., description="File information containing miner hotkey and version number (format: hotkey:version)"), + file_info: str = Form( + ..., description="File information containing miner hotkey and version number (format: hotkey:version)" + ), signature: str = Form(..., description="Signature to verify the authenticity of the upload"), name: str = Form(..., description="Name of the agent"), payment_block_hash: str = Form(..., description="Block hash in which payment was made"), @@ -120,70 +133,60 @@ async def post_agent( ) -> AgentUploadResponse: """ Upload a new agent version for evaluation - + This endpoint allows miners to upload their agent code for evaluation. The agent must: - Be a Python file - Be under 1MB in size - Pass static code safety checks - Pass similarity validation to prevent copying - Be properly signed with the miner's keypair - + Rate limiting may apply based on configuration. """ if config.DISALLOW_UPLOADS: - raise HTTPException( - status_code=503, - detail=config.DISALLOW_UPLOADS_REASON - ) + raise HTTPException(status_code=503, detail=config.DISALLOW_UPLOADS_REASON) # Extract upload attempt data for tracking miner_hotkey = get_miner_hotkey(file_info) agent_file.file.seek(0, 2) file_size_bytes = agent_file.file.tell() agent_file.file.seek(0) - + upload_data = { - 'hotkey': miner_hotkey, - 'agent_name': name, - 'filename': agent_file.filename, - 'file_size_bytes': file_size_bytes, - 'ip_address': getattr(request.client, 'host', None) if request.client else None + "hotkey": miner_hotkey, + "agent_name": name, + "filename": agent_file.filename, + "file_size_bytes": file_size_bytes, + "ip_address": getattr(request.client, "host", None) if request.client else None, } - + try: - logger.debug(f"Platform received a /upload/agent API request. Beginning process handle-upload-agent.") + logger.debug("Platform received a /upload/agent API request. Beginning process handle-upload-agent.") logger.info(f"Uploading agent {name} for miner {miner_hotkey}.") if prod: check_signature(public_key, file_info, signature) await check_hotkey_registered(miner_hotkey) - await check_agent_banned(miner_hotkey=miner_hotkey) + await check_agent_banned(miner_hotkey=miner_hotkey) check_if_python_file(agent_file.filename) if prod: # Verify payment # Check if payment has already been used for an agent existing_payment = await retrieve_payment_by_hash( - payment_block_hash=payment_block_hash, - payment_extrinsic_index=payment_extrinsic_index + payment_block_hash=payment_block_hash, payment_extrinsic_index=payment_extrinsic_index ) if existing_payment is not None: - raise HTTPException( - status_code=402, - detail="Payment already used" - ) + raise HTTPException(status_code=402, detail="Payment already used") # Retrieve payment details from the chain try: payment_block = subtensor.substrate.get_block(block_hash=payment_block_hash) except Exception as e: logger.error(f"Error retrieving payment block: {e}") - raise HTTPException( - status_code=402, - detail="Payment could not be verified" - ) + raise HTTPException(status_code=402, detail="Payment could not be verified") # example payment block: """ @@ -198,9 +201,9 @@ async def post_agent( 'parentHash': '0x1065e83a02ff961d45ac34a6990477de3cba102bbba2322950815e5d59f23135', 'stateRoot': '0x301a04303fb97143649e44ca9c1d674606c8004082d11973c816ff67f2a13998'}} """ - block_number = payment_block['header']['number'] + block_number = payment_block["header"]["number"] coldkey = subtensor.get_hotkey_owner(hotkey_ss58=miner_hotkey, block=int(block_number)) - payment_extrinsic = payment_block['extrinsics'][int(payment_extrinsic_index)] + payment_extrinsic = payment_block["extrinsics"][int(payment_extrinsic_index)] payment_cost = await get_upload_price(cache_time=payment_time) @@ -209,40 +212,31 @@ async def post_agent( """ payment_value = None - for arg in payment_extrinsic.value['call']['call_args']: - if arg['name'] == 'value': - payment_value = arg['value'] + for arg in payment_extrinsic.value["call"]["call_args"]: + if arg["name"] == "value": + payment_value = arg["value"] break if payment_value is None or check_if_extrinsic_failed(payment_block_hash, int(payment_extrinsic_index)): - raise HTTPException( - status_code=402, - detail="Payment value not found" - ) + raise HTTPException(status_code=402, detail="Payment value not found") if payment_value != payment_cost.amount_rao: - raise HTTPException( - status_code=402, - detail="Payment amount does not match" - ) + raise HTTPException(status_code=402, detail="Payment amount does not match") # Make sure coldkey is the same as hotkeys owner coldkey - if coldkey != payment_extrinsic['address']: - raise HTTPException( - status_code=402, - detail="Coldkey does not match" - ) + if coldkey != payment_extrinsic["address"]: + raise HTTPException(status_code=402, detail="Coldkey does not match") # Make sure destination is our upload send address destination = None - for arg in payment_extrinsic.value['call']['call_args']: - if arg['name'] == 'dest': - destination = arg['value'] + for arg in payment_extrinsic.value["call"]["call_args"]: + if arg["name"] == "dest": + destination = arg["value"] break if destination != config.UPLOAD_SEND_ADDRESS: raise HTTPException( status_code=402, - detail=f"Destination does not match. The payment should be sent to {config.UPLOAD_SEND_ADDRESS}" + detail=f"Destination does not match. The payment should be sent to {config.UPLOAD_SEND_ADDRESS}", ) agent_text = (await agent_file.read()).decode("utf-8") @@ -250,9 +244,11 @@ async def post_agent( hotkey_lock = await get_hotkey_lock(miner_hotkey) async with DebugLock(hotkey_lock, f"Agent upload lock for miner {miner_hotkey}"): latest_agent: Optional[Agent] = await get_latest_agent_for_miner_hotkey(miner_hotkey=miner_hotkey) - - latest_agent_created_at_in_latest_set_id = await get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id(miner_hotkey=miner_hotkey) - + + latest_agent_created_at_in_latest_set_id = ( + await get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id(miner_hotkey=miner_hotkey) + ) + if prod and latest_agent_created_at_in_latest_set_id: check_rate_limit(latest_agent_created_at_in_latest_set_id) agent = Agent( @@ -273,30 +269,29 @@ async def post_agent( amount_rao=payment_value, agent_id=agent.agent_id, miner_hotkey=miner_hotkey, - miner_coldkey=coldkey + miner_coldkey=coldkey, ) logger.info(f"Successfully uploaded agent {agent.agent_id} for miner {miner_hotkey}.") # Record successful upload - await record_upload_attempt( - upload_type="agent", - success=True, - agent_id=agent.agent_id, - **upload_data - ) + await record_upload_attempt(upload_type="agent", success=True, agent_id=agent.agent_id, **upload_data) return AgentUploadResponse( - status="success", - message=f"Successfully uploaded agent {agent.agent_id} for miner {miner_hotkey}." + status="success", message=f"Successfully uploaded agent {agent.agent_id} for miner {miner_hotkey}." ) - + except HTTPException as e: # Determine error type and get ban reason if applicable - error_type = 'banned' if e.status_code == 403 and 'banned' in e.detail.lower() else \ - 'rate_limit' if e.status_code == 429 else 'validation_error' - banned_hotkey = await get_banned_hotkey(miner_hotkey) if error_type == 'banned' and miner_hotkey else None - + error_type = ( + "banned" + if e.status_code == 403 and "banned" in e.detail.lower() + else "rate_limit" + if e.status_code == 429 + else "validation_error" + ) + banned_hotkey = await get_banned_hotkey(miner_hotkey) if error_type == "banned" and miner_hotkey else None + # Record failed upload attempt await record_upload_attempt( upload_type="agent", @@ -305,36 +300,34 @@ async def post_agent( error_message=e.detail, ban_reason=banned_hotkey.banned_reason if banned_hotkey else None, http_status_code=e.status_code, - **upload_data + **upload_data, ) raise - + except Exception as e: # Record internal error await record_upload_attempt( upload_type="agent", success=False, - error_type='internal_error', + error_type="internal_error", error_message=str(e), http_status_code=500, - **upload_data + **upload_data, ) raise class UploadPriceResponse(BaseModel): """Response model for successful agent upload""" + amount_rao: int = Field(..., description="Amount to send for evaluation (in RAO)") send_address: str = Field(..., description="TAO address to send evaluation payment to") -@router.get( - "/eval-pricing", - tags=["eval-pricing"], - response_model=UploadPriceResponse -) + +@router.get("/eval-pricing", tags=["eval-pricing"], response_model=UploadPriceResponse) @hourly_cache() async def get_upload_price() -> UploadPriceResponse: - TAO_PRICE = await get_tao_price() + TAO_PRICE = await get_tao_price() eval_cost_usd = 30 # Get the amount of tao required per eval @@ -344,10 +337,8 @@ async def get_upload_price() -> UploadPriceResponse: # This also makes production evals more expensive than local by a good margin to discourage testing in production and variance farming amount_rao = int(eval_cost_tao * 1e9 * 1.4) - return UploadPriceResponse( - amount_rao=amount_rao, - send_address=config.UPLOAD_SEND_ADDRESS - ) + return UploadPriceResponse(amount_rao=amount_rao, send_address=config.UPLOAD_SEND_ADDRESS) + def check_if_extrinsic_failed(block_hash: str, extrinsic_index: int) -> bool: events = subtensor.substrate.get_events(block_hash=block_hash) diff --git a/api/src/main.py b/api/src/main.py index 4e90f925f..4cdf301fe 100644 --- a/api/src/main.py +++ b/api/src/main.py @@ -2,40 +2,29 @@ import asyncio +import uvicorn from fastapi import FastAPI from fastapi.concurrency import asynccontextmanager from fastapi.middleware.cors import CORSMiddleware -import uvicorn - -from api.loops.fetch_metagraph import fetch_metagraph_loop -from api.loops.validator_heartbeat_timeout import validator_heartbeat_timeout_loop - - -from queries.evaluation import set_all_unfinished_evaluation_runs_to_errored -from api.src.endpoints.upload import router as upload_router - -# NEW fixed endpoints -from api.endpoints.validator import router as validator_router -from api.endpoints.debug import router as debug_router +import api.config as config from api.endpoints.agent import router as agent_router +from api.endpoints.debug import router as debug_router from api.endpoints.evaluation_run import router as evaluation_run_router -from api.endpoints.evaluations import router as evaluations_router from api.endpoints.evaluation_sets import router as evaluation_sets_router +from api.endpoints.evaluations import router as evaluations_router +from api.endpoints.retrieval import router as retrieval_router from api.endpoints.scoring import router as scoring_router from api.endpoints.statistics import router as statistics_router -from api.endpoints.retrieval import router as retrieval_router - - - - - - -import api.config as config - -from utils.s3 import initialize_s3, deinitialize_s3 -from utils.database import initialize_database, deinitialize_database +# NEW fixed endpoints +from api.endpoints.validator import router as validator_router +from api.loops.fetch_metagraph import fetch_metagraph_loop +from api.loops.validator_heartbeat_timeout import validator_heartbeat_timeout_loop +from api.src.endpoints.upload import router as upload_router +from queries.evaluation import set_all_unfinished_evaluation_runs_to_errored +from utils.database import deinitialize_database, initialize_database +from utils.s3 import deinitialize_s3, initialize_s3 @asynccontextmanager @@ -46,7 +35,7 @@ async def lifespan(app: FastAPI): password=config.DATABASE_PASSWORD, host=config.DATABASE_HOST, port=config.DATABASE_PORT, - name=config.DATABASE_NAME + name=config.DATABASE_NAME, ) # S3 setup @@ -54,54 +43,34 @@ async def lifespan(app: FastAPI): _bucket=config.S3_BUCKET_NAME, region=config.AWS_REGION, access_key_id=config.AWS_ACCESS_KEY_ID, - secret_access_key=config.AWS_SECRET_ACCESS_KEY + secret_access_key=config.AWS_SECRET_ACCESS_KEY, ) # Loops - if config.SHOULD_RUN_LOOPS: # validator loops; TODO: rename env var + if config.SHOULD_RUN_LOOPS: # validator loops; TODO: rename env var asyncio.create_task(validator_heartbeat_timeout_loop()) - - asyncio.create_task(fetch_metagraph_loop()) - + asyncio.create_task(fetch_metagraph_loop()) # TODO ADAM: fix this, the error message isn't useful and it sets it to a 2xxx error when it should be a 3xxx error - await set_all_unfinished_evaluation_runs_to_errored( - error_message="Platform crashed while running this evaluation" - ) - + await set_all_unfinished_evaluation_runs_to_errored(error_message="Platform crashed while running this evaluation") yield - - await deinitialize_database() await deinitialize_s3() - app = FastAPI(lifespan=lifespan) - - - - - - - - - - - - # Configure CORS app.add_middleware( CORSMiddleware, - allow_origins=["http://localhost:3000", 'https://www.ridges.ai'], + allow_origins=["http://localhost:3000", "https://www.ridges.ai"], allow_credentials=True, allow_methods=["*"], - allow_headers=["*"] + allow_headers=["*"], ) app.include_router(upload_router, prefix="/upload") @@ -117,4 +86,4 @@ async def lifespan(app: FastAPI): if __name__ == "__main__": - uvicorn.run(app, host=config.HOST, port=config.PORT) \ No newline at end of file + uvicorn.run(app, host=config.HOST, port=config.PORT) diff --git a/api/src/utils/request_cache.py b/api/src/utils/request_cache.py index 5d77be466..a460a1a5a 100644 --- a/api/src/utils/request_cache.py +++ b/api/src/utils/request_cache.py @@ -1,9 +1,10 @@ -import time import asyncio +import time from datetime import datetime from functools import wraps from typing import Any + def hourly_cache(): def decorator(func): lock = asyncio.Lock() @@ -14,7 +15,7 @@ def decorator(func): async def wrapper(*args, **kwargs): nonlocal hourly_bucket_cache - cache_time = kwargs.pop('cache_time', time.time()) + cache_time = kwargs.pop("cache_time", time.time()) hour_bucket = datetime.fromtimestamp(cache_time).strftime("%Y-%m-%d:%H") if hour_bucket in hourly_bucket_cache: @@ -26,4 +27,5 @@ async def wrapper(*args, **kwargs): return result return wrapper + return decorator diff --git a/api/src/utils/upload_agent_helpers.py b/api/src/utils/upload_agent_helpers.py index 6229b88ec..20bca69b9 100644 --- a/api/src/utils/upload_agent_helpers.py +++ b/api/src/utils/upload_agent_helpers.py @@ -1,13 +1,13 @@ from datetime import datetime, timedelta, timezone -from fastapi import UploadFile, HTTPException +import httpx from bittensor_wallet.keypair import Keypair +from fastapi import HTTPException, UploadFile import utils.logger as logger from api.config import MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS from queries.banned_hotkey import get_banned_hotkey from utils.bittensor import check_if_hotkey_is_registered -from models.agent import Agent def get_miner_hotkey(file_info: str) -> str: @@ -16,25 +16,21 @@ def get_miner_hotkey(file_info: str) -> str: if not miner_hotkey: logger.error(f"A miner attempted to upload an agent without a hotkey. File info: {file_info}.") - raise HTTPException( - status_code=400, - detail="miner_hotkey is required" - ) - + raise HTTPException(status_code=400, detail="miner_hotkey is required") + logger.debug(f"Miner hotkey successfully extracted: {miner_hotkey}.") return miner_hotkey + def check_if_python_file(filename: str) -> None: - logger.debug(f"Checking if the file is a python file...") + logger.debug("Checking if the file is a python file...") if not filename.endswith(".py"): logger.error(f"A miner attempted to upload an agent with an invalid filename: {filename}.") - raise HTTPException( - status_code=400, - detail="File must be a python file" - ) - - logger.debug(f"The file is a python file.") + raise HTTPException(status_code=400, detail="File must be a python file") + + logger.debug("The file is a python file.") + async def check_agent_banned(miner_hotkey: str) -> None: logger.debug(f"Checking if miner hotkey {miner_hotkey} is banned...") @@ -43,75 +39,84 @@ async def check_agent_banned(miner_hotkey: str) -> None: logger.error(f"A miner attempted to upload an agent with a banned hotkey: {miner_hotkey}.") raise HTTPException( status_code=403, - detail="Your miner hotkey has been banned for attempting to obfuscate code or otherwise cheat. If this is in error, please contact us on Discord" + detail="Your miner hotkey has been banned for attempting to obfuscate code or otherwise cheat. If this is in error, please contact us on Discord", ) - + logger.debug(f"Miner hotkey {miner_hotkey} is not banned.") + def check_rate_limit(latest_agent_created_at_in_latest_set_id: datetime) -> None: - logger.debug(f"Checking if miner is rate limited...") + logger.debug("Checking if miner is rate limited...") + + earliest_allowed_time = latest_agent_created_at_in_latest_set_id + timedelta( + seconds=MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS + ) + logger.debug( + f"Earliest allowed time: {earliest_allowed_time}. Current time: {datetime.now(timezone.utc)}. Difference: {datetime.now(timezone.utc) - earliest_allowed_time}. Minimum allowed time: {timedelta(seconds=MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS)}." + ) - earliest_allowed_time = latest_agent_created_at_in_latest_set_id + timedelta(seconds=MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS) - logger.debug(f"Earliest allowed time: {earliest_allowed_time}. Current time: {datetime.now(timezone.utc)}. Difference: {datetime.now(timezone.utc) - earliest_allowed_time}. Minimum allowed time: {timedelta(seconds=MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS)}.") - if datetime.now(timezone.utc) < earliest_allowed_time: - logger.error(f"A miner attempted to upload an agent too quickly. Latest agent created at {latest_agent_created_at_in_latest_set_id} and current time is {datetime.now(timezone.utc)}.") + logger.error( + f"A miner attempted to upload an agent too quickly. Latest agent created at {latest_agent_created_at_in_latest_set_id} and current time is {datetime.now(timezone.utc)}." + ) raise HTTPException( status_code=429, - detail=f"You must wait {MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS} seconds before uploading a new agent version" + detail=f"You must wait {MINER_AGENT_UPLOAD_RATE_LIMIT_SECONDS} seconds before uploading a new agent version", ) - - logger.debug(f"Miner is not rate limited.") + + logger.debug("Miner is not rate limited.") + def check_signature(public_key: str, file_info: str, signature: str) -> None: - logger.debug(f"Checking if the signature is valid...") + logger.debug("Checking if the signature is valid...") logger.debug(f"Public key: {public_key}, File info: {file_info}, Signature: {signature}.") keypair = Keypair(public_key=public_key) if not keypair.verify(file_info, bytes.fromhex(signature)): - logger.error(f"A miner attempted to upload an agent with an invalid signature. Public key: {public_key}, File info: {file_info}, Signature: {signature}.") - raise HTTPException( - status_code=400, - detail="Invalid signature" + logger.error( + f"A miner attempted to upload an agent with an invalid signature. Public key: {public_key}, File info: {file_info}, Signature: {signature}." ) - - logger.debug(f"The signature is valid.") + raise HTTPException(status_code=400, detail="Invalid signature") + + logger.debug("The signature is valid.") + async def check_hotkey_registered(miner_hotkey: str) -> None: logger.debug(f"Checking if miner hotkey {miner_hotkey} is registered on subnet...") if not await check_if_hotkey_is_registered(miner_hotkey): - logger.error(f"A miner attempted to upload an agent with a hotkey that is not registered on subnet: {miner_hotkey}.") - raise HTTPException(status_code=400, detail=f"Hotkey not registered on subnet") - + logger.error( + f"A miner attempted to upload an agent with a hotkey that is not registered on subnet: {miner_hotkey}." + ) + raise HTTPException(status_code=400, detail="Hotkey not registered on subnet") + logger.debug(f"Miner hotkey {miner_hotkey} is registered on the subnet.") - + + async def check_file_size(agent_file: UploadFile) -> str: - logger.debug(f"Checking if the file size is valid...") + logger.debug("Checking if the file size is valid...") - MAX_FILE_SIZE = 2 * 1024 * 1024 + MAX_FILE_SIZE = 2 * 1024 * 1024 file_size = 0 content = b"" for chunk in agent_file.file: file_size += len(chunk) content += chunk if file_size > MAX_FILE_SIZE: - logger.error(f"A miner attempted to upload an agent with a file size that exceeds the maximum allowed size. File size: {file_size}.") - raise HTTPException( - status_code=400, - detail="File size must not exceed 1MB" + logger.error( + f"A miner attempted to upload an agent with a file size that exceeds the maximum allowed size. File size: {file_size}." ) - - logger.debug(f"The file size is valid.") + raise HTTPException(status_code=400, detail="File size must not exceed 1MB") + + logger.debug("The file size is valid.") await agent_file.seek(0) - + # Handle both bytes and string content if isinstance(content, bytes): - return content.decode('utf-8') + return content.decode("utf-8") else: return content -import httpx async def get_tao_price() -> float: url = "https://api.coingecko.com/api/v3/simple/price" diff --git a/evaluator/datasets/polyglot_py/accumulate/main.py b/evaluator/datasets/polyglot_py/accumulate/main.py index 05b3a28af..03cc9aa31 100644 --- a/evaluator/datasets/polyglot_py/accumulate/main.py +++ b/evaluator/datasets/polyglot_py/accumulate/main.py @@ -1,7 +1,8 @@ from typing import Callable, List, TypeVar -T = TypeVar('T') -U = TypeVar('U') +T = TypeVar("T") +U = TypeVar("U") + def accumulate(collection: List[T], operation: Callable[[T], U]) -> List[U]: pass diff --git a/evaluator/datasets/polyglot_py/accumulate/tests.py b/evaluator/datasets/polyglot_py/accumulate/tests.py index 879983286..44a226436 100644 --- a/evaluator/datasets/polyglot_py/accumulate/tests.py +++ b/evaluator/datasets/polyglot_py/accumulate/tests.py @@ -8,32 +8,23 @@ def test_empty_sequence(self): self.assertEqual(accumulate([], lambda x: x / 2), []) def test_pow(self): - self.assertEqual( - accumulate([1, 2, 3, 4, 5], lambda x: x * x), [1, 4, 9, 16, 25]) + self.assertEqual(accumulate([1, 2, 3, 4, 5], lambda x: x * x), [1, 4, 9, 16, 25]) def test_divmod(self): - self.assertEqual( - accumulate([10, 17, 23], lambda x: divmod(x, 7)), - [(1, 3), (2, 3), (3, 2)]) + self.assertEqual(accumulate([10, 17, 23], lambda x: divmod(x, 7)), [(1, 3), (2, 3), (3, 2)]) def test_composition(self): inp = [10, 17, 23] - self.assertEqual( - accumulate( - accumulate(inp, lambda x: divmod(x, 7)), - lambda x: 7 * x[0] + x[1]), inp) + self.assertEqual(accumulate(accumulate(inp, lambda x: divmod(x, 7)), lambda x: 7 * x[0] + x[1]), inp) def test_capitalize(self): - self.assertEqual( - accumulate(['hello', 'world'], str.upper), ['HELLO', 'WORLD']) + self.assertEqual(accumulate(["hello", "world"], str.upper), ["HELLO", "WORLD"]) def test_recursive(self): - inp = ['a', 'b', 'c'] - out = [['a1', 'a2', 'a3'], ['b1', 'b2', 'b3'], ['c1', 'c2', 'c3']] - self.assertEqual( - accumulate( - inp, lambda x: accumulate(list('123'), lambda y: x + y)), out) + inp = ["a", "b", "c"] + out = [["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]] + self.assertEqual(accumulate(inp, lambda x: accumulate(list("123"), lambda y: x + y)), out) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/acronym/solution.py b/evaluator/datasets/polyglot_py/acronym/solution.py index e7cf298dc..51ed1d618 100644 --- a/evaluator/datasets/polyglot_py/acronym/solution.py +++ b/evaluator/datasets/polyglot_py/acronym/solution.py @@ -3,4 +3,4 @@ def abbreviate(words): regex = "[A-Z]+['a-z]*|['a-z]+" - return ''.join(word[0].upper() for word in re.findall(regex, words)) + return "".join(word[0].upper() for word in re.findall(regex, words)) diff --git a/evaluator/datasets/polyglot_py/acronym/tests.py b/evaluator/datasets/polyglot_py/acronym/tests.py index d289fcc0d..91135ca50 100644 --- a/evaluator/datasets/polyglot_py/acronym/tests.py +++ b/evaluator/datasets/polyglot_py/acronym/tests.py @@ -27,9 +27,7 @@ def test_punctuation_without_whitespace(self): def test_very_long_abbreviation(self): self.assertEqual( - abbreviate( - "Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me" - ), + abbreviate("Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me"), "ROTFLSHTMDCOALM", ) diff --git a/evaluator/datasets/polyglot_py/affine-cipher/solution.py b/evaluator/datasets/polyglot_py/affine-cipher/solution.py index 34ca0418d..11bb2126e 100644 --- a/evaluator/datasets/polyglot_py/affine-cipher/solution.py +++ b/evaluator/datasets/polyglot_py/affine-cipher/solution.py @@ -13,7 +13,7 @@ def mod_inverse(a_key, alphabet): def translate(text, a_key, b_key, mode): inverse = mod_inverse(a_key, ALPHABET) if inverse == 1: - raise ValueError('a and m must be coprime.') + raise ValueError("a and m must be coprime.") chars = [] for character in text: @@ -28,13 +28,12 @@ def translate(text, a_key, b_key, mode): new = (inverse * (origin - b_key)) % ALPHABET chars.append(chr(new + 97)) - return ''.join(chars) + return "".join(chars) def encode(plain, a, b): cipher = translate(plain, a, b, 0) - return ' '.join([cipher[idx:idx + BLOCK_SIZE] - for idx in range(0, len(cipher), BLOCK_SIZE)]) + return " ".join([cipher[idx : idx + BLOCK_SIZE] for idx in range(0, len(cipher), BLOCK_SIZE)]) def decode(ciphered, a, b): diff --git a/evaluator/datasets/polyglot_py/affine-cipher/tests.py b/evaluator/datasets/polyglot_py/affine-cipher/tests.py index 94a5fa622..b00094ac0 100644 --- a/evaluator/datasets/polyglot_py/affine-cipher/tests.py +++ b/evaluator/datasets/polyglot_py/affine-cipher/tests.py @@ -27,9 +27,7 @@ def test_encode_mindblowingly(self): self.assertEqual(encode("mindblowingly", 11, 15), "rzcwa gnxzc dgt") def test_encode_numbers(self): - self.assertEqual( - encode("Testing,1 2 3, testing.", 3, 4), "jqgjc rw123 jqgjc rw" - ) + self.assertEqual(encode("Testing,1 2 3, testing.", 3, 4), "jqgjc rw123 jqgjc rw") def test_encode_deep_thought(self): self.assertEqual(encode("Truth is fiction.", 5, 17), "iynia fdqfb ifje") @@ -71,9 +69,7 @@ def test_decode_with_no_spaces_in_input(self): ) def test_decode_with_too_many_spaces(self): - self.assertEqual( - decode("vszzm cly yd cg qdp", 15, 16), "jollygreengiant" - ) + self.assertEqual(decode("vszzm cly yd cg qdp", 15, 16), "jollygreengiant") def test_decode_with_a_not_coprime_to_m(self): with self.assertRaises(ValueError) as err: diff --git a/evaluator/datasets/polyglot_py/all-your-base/main.py b/evaluator/datasets/polyglot_py/all-your-base/main.py index 574162738..4548e99fe 100644 --- a/evaluator/datasets/polyglot_py/all-your-base/main.py +++ b/evaluator/datasets/polyglot_py/all-your-base/main.py @@ -1,4 +1,5 @@ from typing import List + def rebase(input_base: int, digits: List[int], output_base: int) -> List[int]: pass diff --git a/evaluator/datasets/polyglot_py/all-your-base/solution.py b/evaluator/datasets/polyglot_py/all-your-base/solution.py index e493a5024..097c96eb6 100644 --- a/evaluator/datasets/polyglot_py/all-your-base/solution.py +++ b/evaluator/datasets/polyglot_py/all-your-base/solution.py @@ -1,5 +1,5 @@ def from_digits(digits, base): - return sum(number * base ** idx for idx, number in enumerate(reversed(digits))) + return sum(number * base**idx for idx, number in enumerate(reversed(digits))) def to_digits(number, base_to): diff --git a/evaluator/datasets/polyglot_py/all-your-base/tests.py b/evaluator/datasets/polyglot_py/all-your-base/tests.py index f5cec438c..0beb52d63 100644 --- a/evaluator/datasets/polyglot_py/all-your-base/tests.py +++ b/evaluator/datasets/polyglot_py/all-your-base/tests.py @@ -68,17 +68,13 @@ def test_negative_digit(self): with self.assertRaises(ValueError) as err: rebase(2, [1, -1, 1, 0, 1, 0], 10) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "all digits must satisfy 0 <= d < input base" - ) + self.assertEqual(err.exception.args[0], "all digits must satisfy 0 <= d < input base") def test_invalid_positive_digit(self): with self.assertRaises(ValueError) as err: rebase(2, [1, 2, 1, 0, 1, 0], 10) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "all digits must satisfy 0 <= d < input base" - ) + self.assertEqual(err.exception.args[0], "all digits must satisfy 0 <= d < input base") def test_output_base_is_one(self): with self.assertRaises(ValueError) as err: diff --git a/evaluator/datasets/polyglot_py/allergies/main.py b/evaluator/datasets/polyglot_py/allergies/main.py index 92f03acf4..7b2d1d530 100644 --- a/evaluator/datasets/polyglot_py/allergies/main.py +++ b/evaluator/datasets/polyglot_py/allergies/main.py @@ -1,7 +1,7 @@ from typing import List -class Allergies: +class Allergies: def __init__(self, score: int) -> None: pass diff --git a/evaluator/datasets/polyglot_py/allergies/solution.py b/evaluator/datasets/polyglot_py/allergies/solution.py index 44298c974..2e0352640 100644 --- a/evaluator/datasets/polyglot_py/allergies/solution.py +++ b/evaluator/datasets/polyglot_py/allergies/solution.py @@ -1,15 +1,5 @@ class Allergies: - - _allergies = [ - "eggs", - "peanuts", - "shellfish", - "strawberries", - "tomatoes", - "chocolate", - "pollen", - "cats" - ] + _allergies = ["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"] def __init__(self, score): self.score = score @@ -19,5 +9,4 @@ def allergic_to(self, item): @property def lst(self): - return [allergy for allergy in self._allergies - if self.allergic_to(allergy)] + return [allergy for allergy in self._allergies if self.allergic_to(allergy)] diff --git a/evaluator/datasets/polyglot_py/alphametics/solution.py b/evaluator/datasets/polyglot_py/alphametics/solution.py index 3faf17aaa..2001ec542 100644 --- a/evaluator/datasets/polyglot_py/alphametics/solution.py +++ b/evaluator/datasets/polyglot_py/alphametics/solution.py @@ -9,12 +9,12 @@ to reduce the number of permutations """ -from itertools import permutations, chain, product +from itertools import chain, permutations, product def dig_perms(digit_set, non_zero_chars, ok_zero_chars): """This function creates permutations given the set of digits, - letters not alllowed to be 0, and letters allowed to be 0 + letters not alllowed to be 0, and letters allowed to be 0 """ non_zero_count = len(non_zero_chars) # How many letters are non-0 ok_zero_count = len(ok_zero_chars) # How many letters are allowed 0 @@ -43,17 +43,20 @@ def dig_perms(digit_set, non_zero_chars, ok_zero_chars): # first iterator with all non-0 permutations # second iterator with all permulations without 1 letter # insert 0 in all possible positions of that permutation - return chain(permutations(non_zero_digit_set, total_count), - map(lambda iters: iters[0][:iters[1]] + (0,) + iters[0][iters[1]:], - product(permutations(non_zero_digit_set, total_count - 1), - positions_list))) + return chain( + permutations(non_zero_digit_set, total_count), + map( + lambda iters: iters[0][: iters[1]] + (0,) + iters[0][iters[1] :], + product(permutations(non_zero_digit_set, total_count - 1), positions_list), + ), + ) def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): """This function recursively traces a parsed expression from lowest - digits to highest, generating additional digits when necessary - checking the digit sum is divisible by 10, carrying the multiple of 10 - up to the next level + digits to highest, generating additional digits when necessary + checking the digit sum is divisible by 10, carrying the multiple of 10 + up to the next level """ # Basic parameters of the equation, # maximal digit rank @@ -93,8 +96,7 @@ def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): # build the dictionary for the new letters and this level new_dict = dict(zip(digit_letters, newdigs)) # complete the partial sum into test sum using the current permutation - testsum = part_sum + sum([new_dict[caesar] * van_gogh - for caesar, van_gogh in remaining_exp]) + testsum = part_sum + sum([new_dict[caesar] * van_gogh for caesar, van_gogh in remaining_exp]) # check if the sum is divisible by 10 dali, rembrandt = divmod(testsum, 10) if rembrandt == 0: @@ -103,9 +105,7 @@ def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): # proceed to the next level of recursion with # the same eqparams, but updated digit dictionary, # new carry over and remaining digits to assign - recurring_test = check_rec(eqparams, - (new_dict, dali, remaining_digits - set(newdigs)), - power + 1) + recurring_test = check_rec(eqparams, (new_dict, dali, remaining_digits - set(newdigs)), power + 1) # if the recursive call returned a non-empty dictionary # this means the recursion has found a solution # otherwise, proceed to the new permutation @@ -117,14 +117,15 @@ def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): def solve(puzzle): - """A function to solve the alphametics problem - """ + """A function to solve the alphametics problem""" # First, split the expresion into left and right parts by == # split each part into words by + # strip spaces fro, each word, reverse each work to # enumerate the digit rank from lower to higer - full_exp = [list(map(lambda idx: list(reversed(idx.strip())), sigmund.split('+'))) - for sigmund in puzzle.strip().upper().split('==')] + full_exp = [ + list(map(lambda idx: list(reversed(idx.strip())), sigmund.split("+"))) + for sigmund in puzzle.strip().upper().split("==") + ] # Find the maximal lenght of the work, maximal possive digit rank or # the power of 10, should the < maxp max_digit_rank = max([len(warhol) for sigmund in full_exp for warhol in sigmund]) diff --git a/evaluator/datasets/polyglot_py/anagram/solution.py b/evaluator/datasets/polyglot_py/anagram/solution.py index 55929ff46..7b2d530f4 100644 --- a/evaluator/datasets/polyglot_py/anagram/solution.py +++ b/evaluator/datasets/polyglot_py/anagram/solution.py @@ -1,8 +1,10 @@ def find_anagrams(word, candidates): - return [candidate - for candidate in candidates - if _letters(candidate) == _letters(word) - if candidate.lower() != word.lower()] + return [ + candidate + for candidate in candidates + if _letters(candidate) == _letters(word) + if candidate.lower() != word.lower() + ] def _letters(word): diff --git a/evaluator/datasets/polyglot_py/atbash-cipher/solution.py b/evaluator/datasets/polyglot_py/atbash-cipher/solution.py index a358998e0..ad1af2ece 100644 --- a/evaluator/datasets/polyglot_py/atbash-cipher/solution.py +++ b/evaluator/datasets/polyglot_py/atbash-cipher/solution.py @@ -1,18 +1,16 @@ from string import ascii_lowercase - BLOCK_SIZE = 5 trtbl = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) def base_trans(text): - return ''.join([character for character in text if character.isalnum()]).lower().translate(trtbl) + return "".join([character for character in text if character.isalnum()]).lower().translate(trtbl) def encode(plain): cipher = base_trans(plain) - return ' '.join(cipher[idx:idx + BLOCK_SIZE] - for idx in range(0, len(cipher), BLOCK_SIZE)) + return " ".join(cipher[idx : idx + BLOCK_SIZE] for idx in range(0, len(cipher), BLOCK_SIZE)) def decode(ciphered): diff --git a/evaluator/datasets/polyglot_py/atbash-cipher/tests.py b/evaluator/datasets/polyglot_py/atbash-cipher/tests.py index 313a27e00..cf629ed48 100644 --- a/evaluator/datasets/polyglot_py/atbash-cipher/tests.py +++ b/evaluator/datasets/polyglot_py/atbash-cipher/tests.py @@ -60,6 +60,4 @@ def test_decode_with_too_many_spaces(self): self.assertEqual(decode("vc vix r hn"), "exercism") def test_decode_with_no_spaces(self): - self.assertEqual( - decode("zmlyhgzxovrhlugvmzhgvkkrmthglmv"), "anobstacleisoftenasteppingstone" - ) + self.assertEqual(decode("zmlyhgzxovrhlugvmzhgvkkrmthglmv"), "anobstacleisoftenasteppingstone") diff --git a/evaluator/datasets/polyglot_py/bank-account/solution.py b/evaluator/datasets/polyglot_py/bank-account/solution.py index 90ddf31c2..ecd41499b 100644 --- a/evaluator/datasets/polyglot_py/bank-account/solution.py +++ b/evaluator/datasets/polyglot_py/bank-account/solution.py @@ -10,34 +10,34 @@ def __init__(self): def get_balance(self): with self.lock: if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") return self.balance def open(self): if self.is_open: - raise ValueError('account already open') + raise ValueError("account already open") self.is_open = True self.balance = 0 def deposit(self, amount): with self.lock: if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") if amount <= 0: - raise ValueError('amount must be greater than 0') + raise ValueError("amount must be greater than 0") self.balance += amount def withdraw(self, amount): with self.lock: if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") if amount <= 0: - raise ValueError('amount must be greater than 0') + raise ValueError("amount must be greater than 0") if amount > self.balance: - raise ValueError('amount must be less than balance') + raise ValueError("amount must be less than balance") self.balance -= amount def close(self): if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") self.is_open = False diff --git a/evaluator/datasets/polyglot_py/beer-song/solution.py b/evaluator/datasets/polyglot_py/beer-song/solution.py index 4dfa8ea02..d36e419c3 100644 --- a/evaluator/datasets/polyglot_py/beer-song/solution.py +++ b/evaluator/datasets/polyglot_py/beer-song/solution.py @@ -3,35 +3,35 @@ def recite(start, take=1): for idx in range(start, start - take, -1): results.extend(verse(idx)) if idx > start - take + 1: - results.append('') + results.append("") return results def verse(number): return [ - f'{_bottles(number).capitalize()} of beer on the wall, {_bottles(number)} of beer.', - f'{_action(number)}{_next_bottle(number)}' + f"{_bottles(number).capitalize()} of beer on the wall, {_bottles(number)} of beer.", + f"{_action(number)}{_next_bottle(number)}", ] def _action(current_verse): if current_verse == 0: - return 'Go to the store and buy some more, ' + return "Go to the store and buy some more, " else: - return f'Take {"one" if current_verse > 1 else "it"} down and pass it around, ' + return f"Take {'one' if current_verse > 1 else 'it'} down and pass it around, " def _next_bottle(current_verse): - return f'{_bottles(_next_verse(current_verse))} of beer on the wall.' + return f"{_bottles(_next_verse(current_verse))} of beer on the wall." def _bottles(number): if number == 0: - return 'no more bottles' + return "no more bottles" if number == 1: - return '1 bottle' + return "1 bottle" else: - return f'{number} bottles' + return f"{number} bottles" def _next_verse(current_verse): diff --git a/evaluator/datasets/polyglot_py/binary-search-tree/main.py b/evaluator/datasets/polyglot_py/binary-search-tree/main.py index 95a9b20a4..f73f34ded 100644 --- a/evaluator/datasets/polyglot_py/binary-search-tree/main.py +++ b/evaluator/datasets/polyglot_py/binary-search-tree/main.py @@ -5,7 +5,7 @@ def __init__(self, data: str, left: "TreeNode | None" = None, right: "TreeNode | self.right = None def __str__(self): - return f'TreeNode(data={self.data}, left={self.left}, right={self.right})' + return f"TreeNode(data={self.data}, left={self.left}, right={self.right})" class BinarySearchTree: diff --git a/evaluator/datasets/polyglot_py/binary-search-tree/solution.py b/evaluator/datasets/polyglot_py/binary-search-tree/solution.py index fede54dd6..ddcd494bc 100644 --- a/evaluator/datasets/polyglot_py/binary-search-tree/solution.py +++ b/evaluator/datasets/polyglot_py/binary-search-tree/solution.py @@ -5,7 +5,7 @@ def __init__(self, data, left=None, right=None): self.right = right def __str__(self): - return f'TreeNode(data={self.data}, left={self.left}, right={self.right})' + return f"TreeNode(data={self.data}, left={self.left}, right={self.right})" class BinarySearchTree: diff --git a/evaluator/datasets/polyglot_py/binary-search-tree/tests.py b/evaluator/datasets/polyglot_py/binary-search-tree/tests.py index 31a9d3bc1..8e631e806 100644 --- a/evaluator/datasets/polyglot_py/binary-search-tree/tests.py +++ b/evaluator/datasets/polyglot_py/binary-search-tree/tests.py @@ -33,9 +33,7 @@ def test_can_create_complex_tree(self): TreeNode("2", TreeNode("1", None, None), TreeNode("3", None, None)), TreeNode("6", TreeNode("5", None, None), TreeNode("7", None, None)), ) - self.assertTreeEqual( - BinarySearchTree(["4", "2", "6", "1", "3", "5", "7"]).data(), expected - ) + self.assertTreeEqual(BinarySearchTree(["4", "2", "6", "1", "3", "5", "7"]).data(), expected) def test_can_sort_single_number(self): expected = ["2"] @@ -55,9 +53,7 @@ def test_can_sort_if_second_number_is_greater_than_first(self): def test_can_sort_complex_tree(self): expected = ["1", "2", "3", "5", "6", "7"] - self.assertEqual( - BinarySearchTree(["2", "1", "3", "6", "7", "5"]).sorted_data(), expected - ) + self.assertEqual(BinarySearchTree(["2", "1", "3", "6", "7", "5"]).sorted_data(), expected) # Utilities def assertTreeEqual(self, tree_one, tree_two): diff --git a/evaluator/datasets/polyglot_py/binary-search/solution.py b/evaluator/datasets/polyglot_py/binary-search/solution.py index 0bd7be2cb..c5fd05879 100644 --- a/evaluator/datasets/polyglot_py/binary-search/solution.py +++ b/evaluator/datasets/polyglot_py/binary-search/solution.py @@ -9,4 +9,4 @@ def find(search_list, value): low = middle + 1 else: return middle - raise ValueError('value not in array') + raise ValueError("value not in array") diff --git a/evaluator/datasets/polyglot_py/binary-search/tests.py b/evaluator/datasets/polyglot_py/binary-search/tests.py index 5515c6abd..aa305b3bb 100644 --- a/evaluator/datasets/polyglot_py/binary-search/tests.py +++ b/evaluator/datasets/polyglot_py/binary-search/tests.py @@ -28,9 +28,7 @@ def test_finds_a_value_at_the_end_of_an_array(self): def test_finds_a_value_in_an_array_of_odd_length(self): - self.assertEqual( - find([1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144), 9 - ) + self.assertEqual(find([1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144), 9) def test_finds_a_value_in_an_array_of_even_length(self): diff --git a/evaluator/datasets/polyglot_py/binary/solution.py b/evaluator/datasets/polyglot_py/binary/solution.py index 364669062..77fd60814 100644 --- a/evaluator/datasets/polyglot_py/binary/solution.py +++ b/evaluator/datasets/polyglot_py/binary/solution.py @@ -1,5 +1,4 @@ def parse_binary(digits): - if set(digits) - set('01'): - raise ValueError('Invalid binary literal: ' + digits) - return sum(int(digit) * 2 ** idx - for (idx, digit) in enumerate(reversed(digits))) + if set(digits) - set("01"): + raise ValueError("Invalid binary literal: " + digits) + return sum(int(digit) * 2**idx for (idx, digit) in enumerate(reversed(digits))) diff --git a/evaluator/datasets/polyglot_py/binary/tests.py b/evaluator/datasets/polyglot_py/binary/tests.py index 7186c94a6..140543329 100644 --- a/evaluator/datasets/polyglot_py/binary/tests.py +++ b/evaluator/datasets/polyglot_py/binary/tests.py @@ -4,6 +4,7 @@ If the argument to parse_binary isn't a valid binary number the function should raise a ValueError with a meaningful error message. """ + import unittest from main import parse_binary @@ -55,5 +56,6 @@ def test_invalid_binary_text_with_numbers(self): self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "Invalid binary literal: nope10") -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/bob/solution.py b/evaluator/datasets/polyglot_py/bob/solution.py index 630ccccc6..5f9b0274a 100644 --- a/evaluator/datasets/polyglot_py/bob/solution.py +++ b/evaluator/datasets/polyglot_py/bob/solution.py @@ -2,20 +2,20 @@ def response(hey_bob): hey_bob = hey_bob.strip() if _is_silence(hey_bob): - return 'Fine. Be that way!' + return "Fine. Be that way!" if _is_shouting(hey_bob): if _is_question(hey_bob): return "Calm down, I know what I'm doing!" else: - return 'Whoa, chill out!' + return "Whoa, chill out!" elif _is_question(hey_bob): - return 'Sure.' + return "Sure." else: - return 'Whatever.' + return "Whatever." def _is_silence(hey_bob): - return hey_bob == '' + return hey_bob == "" def _is_shouting(hey_bob): @@ -23,4 +23,4 @@ def _is_shouting(hey_bob): def _is_question(hey_bob): - return hey_bob.endswith('?') + return hey_bob.endswith("?") diff --git a/evaluator/datasets/polyglot_py/bob/tests.py b/evaluator/datasets/polyglot_py/bob/tests.py index 3fd8f4cff..70543b7fb 100644 --- a/evaluator/datasets/polyglot_py/bob/tests.py +++ b/evaluator/datasets/polyglot_py/bob/tests.py @@ -20,9 +20,7 @@ def test_shouting_gibberish(self): self.assertEqual(response("FCECDFCAAB"), "Whoa, chill out!") def test_asking_a_question(self): - self.assertEqual( - response("Does this cryogenic chamber make me look fat?"), "Sure." - ) + self.assertEqual(response("Does this cryogenic chamber make me look fat?"), "Sure.") def test_asking_a_numeric_question(self): self.assertEqual(response("You are, what, like 15?"), "Sure.") @@ -34,14 +32,10 @@ def test_talking_forcefully(self): self.assertEqual(response("Hi there!"), "Whatever.") def test_using_acronyms_in_regular_speech(self): - self.assertEqual( - response("It's OK if you don't want to go work for NASA."), "Whatever." - ) + self.assertEqual(response("It's OK if you don't want to go work for NASA."), "Whatever.") def test_forceful_question(self): - self.assertEqual( - response("WHAT'S GOING ON?"), "Calm down, I know what I'm doing!" - ) + self.assertEqual(response("WHAT'S GOING ON?"), "Calm down, I know what I'm doing!") def test_shouting_numbers(self): self.assertEqual(response("1, 2, 3 GO!"), "Whoa, chill out!") @@ -83,19 +77,13 @@ def test_starting_with_whitespace(self): self.assertEqual(response(" hmmmmmmm..."), "Whatever.") def test_ending_with_whitespace(self): - self.assertEqual( - response("Okay if like my spacebar quite a bit? "), "Sure." - ) + self.assertEqual(response("Okay if like my spacebar quite a bit? "), "Sure.") def test_other_whitespace(self): self.assertEqual(response("\n\r \t"), "Fine. Be that way!") def test_non_question_ending_with_whitespace(self): - self.assertEqual( - response("This is a statement ending with whitespace "), "Whatever." - ) + self.assertEqual(response("This is a statement ending with whitespace "), "Whatever.") def test_multiple_line_question(self): - self.assertEqual( - response("\nDoes this cryogenic chamber make\n me look fat?"), "Sure." - ) + self.assertEqual(response("\nDoes this cryogenic chamber make\n me look fat?"), "Sure.") diff --git a/evaluator/datasets/polyglot_py/book-store/main.py b/evaluator/datasets/polyglot_py/book-store/main.py index 77b4b551e..f7d26843a 100644 --- a/evaluator/datasets/polyglot_py/book-store/main.py +++ b/evaluator/datasets/polyglot_py/book-store/main.py @@ -1,2 +1,2 @@ -def total(basket: list[int]) -> int: # in cents +def total(basket: list[int]) -> int: # in cents pass diff --git a/evaluator/datasets/polyglot_py/bottle-song/solution.py b/evaluator/datasets/polyglot_py/bottle-song/solution.py index 18e1167da..7f4bfaa3a 100644 --- a/evaluator/datasets/polyglot_py/bottle-song/solution.py +++ b/evaluator/datasets/polyglot_py/bottle-song/solution.py @@ -1,34 +1,46 @@ -NUMBERS = {10: "ten", 9: "nine", 8: "eight", 7: "seven", 6: "six", 5: "five", 4: "four", 3: "three", 2: "two", 1: "one", 0: "no"} +NUMBERS = { + 10: "ten", + 9: "nine", + 8: "eight", + 7: "seven", + 6: "six", + 5: "five", + 4: "four", + 3: "three", + 2: "two", + 1: "one", + 0: "no", +} + def recite(start, take=1): results = [] for idx in range(start, start - take, -1): results.extend(verse(idx)) if idx > start - take + 1: - results.append('') + results.append("") return results def verse(number): - return [ - *main_verse(number), - "And if one green bottle should accidentally fall,", - last_verse(number) - ] + return [*main_verse(number), "And if one green bottle should accidentally fall,", last_verse(number)] + def main_verse(number): if number == 1: return [ - f'One green bottle hanging on the wall,', - f'One green bottle hanging on the wall,', + "One green bottle hanging on the wall,", + "One green bottle hanging on the wall,", ] else: return [ - f'{NUMBERS[number].capitalize()} green bottles hanging on the wall,', - f'{NUMBERS[number].capitalize()} green bottles hanging on the wall,',] + f"{NUMBERS[number].capitalize()} green bottles hanging on the wall,", + f"{NUMBERS[number].capitalize()} green bottles hanging on the wall,", + ] + def last_verse(number): - if number -1 == 1: - return f"There'll be one green bottle hanging on the wall." + if number - 1 == 1: + return "There'll be one green bottle hanging on the wall." else: - return f"There'll be {NUMBERS[number-1]} green bottles hanging on the wall." + return f"There'll be {NUMBERS[number - 1]} green bottles hanging on the wall." diff --git a/evaluator/datasets/polyglot_py/bowling/solution.py b/evaluator/datasets/polyglot_py/bowling/solution.py index 7afb0d63f..7fe4d82e8 100644 --- a/evaluator/datasets/polyglot_py/bowling/solution.py +++ b/evaluator/datasets/polyglot_py/bowling/solution.py @@ -59,26 +59,23 @@ def next_throws(self, frame_idx): def roll_bonus(self, pins): tenth_frame = self.frames[-1] if tenth_frame.is_open(): - raise IndexError('cannot throw bonus with an open tenth frame') + raise IndexError("cannot throw bonus with an open tenth frame") self.bonus_throws.append(pins) # Check against invalid fill balls, e.g. [3, 10] - if (len(self.bonus_throws) == 2 and self.bonus_throws[0] != 10 and - sum(self.bonus_throws) > 10): - raise ValueError('invalid fill balls') + if len(self.bonus_throws) == 2 and self.bonus_throws[0] != 10 and sum(self.bonus_throws) > 10: + raise ValueError("invalid fill balls") # Check if there are more bonuses than it should be if tenth_frame.is_strike() and len(self.bonus_throws) > 2: - raise IndexError( - 'wrong number of fill balls when the tenth frame is a strike') + raise IndexError("wrong number of fill balls when the tenth frame is a strike") elif tenth_frame.is_spare() and len(self.bonus_throws) > 1: - raise IndexError( - 'wrong number of fill balls when the tenth frame is a spare') + raise IndexError("wrong number of fill balls when the tenth frame is a spare") def roll(self, pins): if not 0 <= pins <= 10: - raise ValueError('invalid pins') + raise ValueError("invalid pins") elif self.current_frame_idx == MAX_FRAME: self.roll_bonus(pins) else: @@ -88,12 +85,9 @@ def roll(self, pins): def score(self): if self.current_frame_idx < MAX_FRAME: - raise IndexError('frame less than 10') + raise IndexError("frame less than 10") if self.frames[-1].is_spare() and len(self.bonus_throws) != 1: - raise IndexError( - 'one bonus must be rolled when the tenth frame is spare') + raise IndexError("one bonus must be rolled when the tenth frame is spare") if self.frames[-1].is_strike() and len(self.bonus_throws) != 2: - raise IndexError( - 'two bonuses must be rolled when the tenth frame is strike') - return sum(frame.score(self.next_throws(frame.idx)) - for frame in self.frames) + raise IndexError("two bonuses must be rolled when the tenth frame is strike") + return sum(frame.score(self.next_throws(frame.idx)) for frame in self.frames) diff --git a/evaluator/datasets/polyglot_py/change/solution.py b/evaluator/datasets/polyglot_py/change/solution.py index b62fa0cc1..03c562643 100644 --- a/evaluator/datasets/polyglot_py/change/solution.py +++ b/evaluator/datasets/polyglot_py/change/solution.py @@ -20,6 +20,6 @@ def find_fewest_coins(coins, target): last_coin_value = target array = [] while last_coin[last_coin_value] != -1: - array.append(last_coin_value-last_coin[last_coin_value]) + array.append(last_coin_value - last_coin[last_coin_value]) last_coin_value = last_coin[last_coin_value] return array diff --git a/evaluator/datasets/polyglot_py/circular-buffer/main.py b/evaluator/datasets/polyglot_py/circular-buffer/main.py index 05c62d1c0..930b998b1 100644 --- a/evaluator/datasets/polyglot_py/circular-buffer/main.py +++ b/evaluator/datasets/polyglot_py/circular-buffer/main.py @@ -4,6 +4,7 @@ class BufferFullException(BufferError): message: explanation of the error. """ + def __init__(self, message: str): pass @@ -14,6 +15,7 @@ class BufferEmptyException(BufferError): message: explanation of the error. """ + def __init__(self, message: str): pass diff --git a/evaluator/datasets/polyglot_py/circular-buffer/solution.py b/evaluator/datasets/polyglot_py/circular-buffer/solution.py index 538cc7bc5..bce894327 100644 --- a/evaluator/datasets/polyglot_py/circular-buffer/solution.py +++ b/evaluator/datasets/polyglot_py/circular-buffer/solution.py @@ -4,6 +4,7 @@ class BufferFullException(BufferError): message: explanation of the error. """ + def __init__(self, message): self.message = message @@ -14,12 +15,12 @@ class BufferEmptyException(BufferError): message: explanation of the error. """ + def __init__(self, message): self.message = message class CircularBuffer: - def __init__(self, capacity): self.buffer = bytearray(capacity) self.read_point = 0 @@ -37,7 +38,7 @@ def clear(self): def write(self, data): if all(self.buffer): - raise BufferFullException('Circular buffer is full') + raise BufferFullException("Circular buffer is full") self._update_buffer(data) self.write_point = (self.write_point + 1) % len(self.buffer) @@ -49,7 +50,7 @@ def overwrite(self, data): def read(self): if not any(self.buffer): - raise BufferEmptyException('Circular buffer is empty') + raise BufferEmptyException("Circular buffer is empty") data = chr(self.buffer[self.read_point]) self.buffer[self.read_point] = 0 self.read_point = (self.read_point + 1) % len(self.buffer) diff --git a/evaluator/datasets/polyglot_py/circular-buffer/tests.py b/evaluator/datasets/polyglot_py/circular-buffer/tests.py index d91dbc7f7..0458733bf 100644 --- a/evaluator/datasets/polyglot_py/circular-buffer/tests.py +++ b/evaluator/datasets/polyglot_py/circular-buffer/tests.py @@ -5,9 +5,9 @@ import unittest from main import ( - CircularBuffer, BufferEmptyException, BufferFullException, + CircularBuffer, ) diff --git a/evaluator/datasets/polyglot_py/clock/main.py b/evaluator/datasets/polyglot_py/clock/main.py index 3043cb1c6..15737a6d0 100644 --- a/evaluator/datasets/polyglot_py/clock/main.py +++ b/evaluator/datasets/polyglot_py/clock/main.py @@ -8,11 +8,11 @@ def __repr__(self) -> str: def __str__(self) -> str: pass - def __eq__(self, other: 'Clock') -> bool: + def __eq__(self, other: "Clock") -> bool: pass - def __add__(self, minutes: int) -> 'Clock': + def __add__(self, minutes: int) -> "Clock": pass - def __sub__(self, minutes: int) -> 'Clock': + def __sub__(self, minutes: int) -> "Clock": pass diff --git a/evaluator/datasets/polyglot_py/clock/solution.py b/evaluator/datasets/polyglot_py/clock/solution.py index 47d526816..25ddfc1af 100644 --- a/evaluator/datasets/polyglot_py/clock/solution.py +++ b/evaluator/datasets/polyglot_py/clock/solution.py @@ -7,10 +7,10 @@ def __init__(self, hour, minute): self.cleanup() def __repr__(self): - return f'Clock({self.hour}, {self.minute})' + return f"Clock({self.hour}, {self.minute})" def __str__(self): - return '{:02d}:{:02d}'.format(self.hour, self.minute) + return "{:02d}:{:02d}".format(self.hour, self.minute) def __eq__(self, other): return repr(self) == repr(other) diff --git a/evaluator/datasets/polyglot_py/collatz-conjecture/solution.py b/evaluator/datasets/polyglot_py/collatz-conjecture/solution.py index 843fe43e6..2ddcc546a 100644 --- a/evaluator/datasets/polyglot_py/collatz-conjecture/solution.py +++ b/evaluator/datasets/polyglot_py/collatz-conjecture/solution.py @@ -1,6 +1,6 @@ def steps(number): if number <= 0: - raise ValueError('Only positive integers are allowed') + raise ValueError("Only positive integers are allowed") step_count = 0 while number > 1: diff --git a/evaluator/datasets/polyglot_py/complex-numbers/main.py b/evaluator/datasets/polyglot_py/complex-numbers/main.py index 285a1d4e2..b7d8f8200 100644 --- a/evaluator/datasets/polyglot_py/complex-numbers/main.py +++ b/evaluator/datasets/polyglot_py/complex-numbers/main.py @@ -2,26 +2,26 @@ class ComplexNumber: def __init__(self, real: float, imaginary: float): pass - def __eq__(self, other: 'ComplexNumber') -> bool: + def __eq__(self, other: "ComplexNumber") -> bool: pass - def __add__(self, other: 'ComplexNumber') -> 'ComplexNumber': + def __add__(self, other: "ComplexNumber") -> "ComplexNumber": pass - def __mul__(self, other: 'ComplexNumber') -> 'ComplexNumber': + def __mul__(self, other: "ComplexNumber") -> "ComplexNumber": pass - def __sub__(self, other: 'ComplexNumber') -> 'ComplexNumber': + def __sub__(self, other: "ComplexNumber") -> "ComplexNumber": pass - def __truediv__(self, other: 'ComplexNumber') -> 'ComplexNumber': + def __truediv__(self, other: "ComplexNumber") -> "ComplexNumber": pass def __abs__(self) -> float: pass - def conjugate(self) -> 'ComplexNumber': + def conjugate(self) -> "ComplexNumber": pass - def exp(self) -> 'ComplexNumber': + def exp(self) -> "ComplexNumber": pass diff --git a/evaluator/datasets/polyglot_py/complex-numbers/solution.py b/evaluator/datasets/polyglot_py/complex-numbers/solution.py index b833c38fc..5141c5d25 100644 --- a/evaluator/datasets/polyglot_py/complex-numbers/solution.py +++ b/evaluator/datasets/polyglot_py/complex-numbers/solution.py @@ -1,5 +1,6 @@ import math + class ComplexNumber: def __init__(self, real=0, imaginary=0): self.real = real diff --git a/evaluator/datasets/polyglot_py/complex-numbers/tests.py b/evaluator/datasets/polyglot_py/complex-numbers/tests.py index 61ec78c51..417fab8fd 100644 --- a/evaluator/datasets/polyglot_py/complex-numbers/tests.py +++ b/evaluator/datasets/polyglot_py/complex-numbers/tests.py @@ -11,7 +11,6 @@ class ComplexNumbersTest(unittest.TestCase): - # Real part def test_real_part_of_a_purely_real_number(self): @@ -35,9 +34,7 @@ def test_imaginary_part_of_a_number_with_real_and_imaginary_part(self): self.assertEqual(ComplexNumber(1, 2).imaginary, 2) def test_imaginary_unit(self): - self.assertEqual( - ComplexNumber(0, 1) * ComplexNumber(0, 1), ComplexNumber(-1, 0) - ) + self.assertEqual(ComplexNumber(0, 1) * ComplexNumber(0, 1), ComplexNumber(-1, 0)) # Arithmetic @@ -55,19 +52,13 @@ def test_add_numbers_with_real_and_imaginary_part(self): # Subtraction def test_subtract_purely_real_numbers(self): - self.assertEqual( - ComplexNumber(1, 0) - ComplexNumber(2, 0), ComplexNumber(-1, 0) - ) + self.assertEqual(ComplexNumber(1, 0) - ComplexNumber(2, 0), ComplexNumber(-1, 0)) def test_subtract_purely_imaginary_numbers(self): - self.assertEqual( - ComplexNumber(0, 1) - ComplexNumber(0, 2), ComplexNumber(0, -1) - ) + self.assertEqual(ComplexNumber(0, 1) - ComplexNumber(0, 2), ComplexNumber(0, -1)) def test_subtract_numbers_with_real_and_imaginary_part(self): - self.assertEqual( - ComplexNumber(1, 2) - ComplexNumber(3, 4), ComplexNumber(-2, -2) - ) + self.assertEqual(ComplexNumber(1, 2) - ComplexNumber(3, 4), ComplexNumber(-2, -2)) # Multiplication @@ -75,31 +66,21 @@ def test_multiply_purely_real_numbers(self): self.assertEqual(ComplexNumber(1, 0) * ComplexNumber(2, 0), ComplexNumber(2, 0)) def test_multiply_purely_imaginary_numbers(self): - self.assertEqual( - ComplexNumber(0, 1) * ComplexNumber(0, 2), ComplexNumber(-2, 0) - ) + self.assertEqual(ComplexNumber(0, 1) * ComplexNumber(0, 2), ComplexNumber(-2, 0)) def test_multiply_numbers_with_real_and_imaginary_part(self): - self.assertEqual( - ComplexNumber(1, 2) * ComplexNumber(3, 4), ComplexNumber(-5, 10) - ) + self.assertEqual(ComplexNumber(1, 2) * ComplexNumber(3, 4), ComplexNumber(-5, 10)) # Division def test_divide_purely_real_numbers(self): - self.assertAlmostEqual( - ComplexNumber(1, 0) / ComplexNumber(2, 0), ComplexNumber(0.5, 0) - ) + self.assertAlmostEqual(ComplexNumber(1, 0) / ComplexNumber(2, 0), ComplexNumber(0.5, 0)) def test_divide_purely_imaginary_numbers(self): - self.assertAlmostEqual( - ComplexNumber(0, 1) / ComplexNumber(0, 2), ComplexNumber(0.5, 0) - ) + self.assertAlmostEqual(ComplexNumber(0, 1) / ComplexNumber(0, 2), ComplexNumber(0.5, 0)) def test_divide_numbers_with_real_and_imaginary_part(self): - self.assertAlmostEqual( - ComplexNumber(1, 2) / ComplexNumber(3, 4), ComplexNumber(0.44, 0.08) - ) + self.assertAlmostEqual(ComplexNumber(1, 2) / ComplexNumber(3, 4), ComplexNumber(0.44, 0.08)) # Absolute value @@ -145,14 +126,10 @@ def test_exponential_of_a_purely_real_number(self): self.assertAlmostEqual(ComplexNumber(1, 0).exp(), ComplexNumber(math.e, 0)) def test_exponential_of_a_number_with_real_and_imaginary_part(self): - self.assertAlmostEqual( - ComplexNumber(math.log(2), math.pi).exp(), ComplexNumber(-2, 0) - ) + self.assertAlmostEqual(ComplexNumber(math.log(2), math.pi).exp(), ComplexNumber(-2, 0)) def test_exponential_resulting_in_a_number_with_real_and_imaginary_part(self): - self.assertAlmostEqual( - ComplexNumber(math.log(2) / 2, math.pi / 4).exp(), ComplexNumber(1, 1) - ) + self.assertAlmostEqual(ComplexNumber(math.log(2) / 2, math.pi / 4).exp(), ComplexNumber(1, 1)) # Operations between real numbers and complex numbers diff --git a/evaluator/datasets/polyglot_py/connect/main.py b/evaluator/datasets/polyglot_py/connect/main.py index 163c31131..623741281 100644 --- a/evaluator/datasets/polyglot_py/connect/main.py +++ b/evaluator/datasets/polyglot_py/connect/main.py @@ -1,4 +1,3 @@ - class ConnectGame: def __init__(self, board: str): pass diff --git a/evaluator/datasets/polyglot_py/connect/solution.py b/evaluator/datasets/polyglot_py/connect/solution.py index 0cb003348..412e42395 100644 --- a/evaluator/datasets/polyglot_py/connect/solution.py +++ b/evaluator/datasets/polyglot_py/connect/solution.py @@ -1,9 +1,7 @@ - class ConnectGame: - DIRECTIONS = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, -1), (-1, 1)] - WHITE = 'O' - BLACK = 'X' + WHITE = "O" + BLACK = "X" def __init__(self, lines): self.board = ConnectGame.make_board(lines) @@ -21,7 +19,7 @@ def valid(self, width, height): @staticmethod def make_board(lines): - return [''.join(cur_line.split()) for cur_line in lines.splitlines()] + return ["".join(cur_line.split()) for cur_line in lines.splitlines()] def player_reach_dest(self, player, width, height): if player == self.BLACK: @@ -63,4 +61,4 @@ def get_winner(self): return self.BLACK if self.check_player_is_winner(self.WHITE): return self.WHITE - return '' + return "" diff --git a/evaluator/datasets/polyglot_py/crypto-square/solution.py b/evaluator/datasets/polyglot_py/crypto-square/solution.py index 8cfc7642f..3b1256797 100644 --- a/evaluator/datasets/polyglot_py/crypto-square/solution.py +++ b/evaluator/datasets/polyglot_py/crypto-square/solution.py @@ -1,20 +1,17 @@ -from math import ceil, sqrt from itertools import zip_longest +from math import ceil, sqrt def cipher_text(plain_text): plain_text = _cleanse(plain_text) square_size = int(ceil(sqrt(len(plain_text)))) square = _chunks_of(plain_text, square_size) - return ' '.join([''.join(column) - for column in zip_longest(*square, fillvalue=' ')]) + return " ".join(["".join(column) for column in zip_longest(*square, fillvalue=" ")]) def _cleanse(text): - """Lowercase a string and remove punctuation and whitespace - """ - return ''.join([character for character in text - if character.isalnum()]).lower() + """Lowercase a string and remove punctuation and whitespace""" + return "".join([character for character in text if character.isalnum()]).lower() def _chunks_of(text, num): diff --git a/evaluator/datasets/polyglot_py/custom-set/main.py b/evaluator/datasets/polyglot_py/custom-set/main.py index 6b67c378d..690d7fa4b 100644 --- a/evaluator/datasets/polyglot_py/custom-set/main.py +++ b/evaluator/datasets/polyglot_py/custom-set/main.py @@ -8,23 +8,23 @@ def isempty(self) -> bool: def __contains__(self, element) -> bool: pass - def issubset(self, other: 'CustomSet') -> bool: + def issubset(self, other: "CustomSet") -> bool: pass - def isdisjoint(self, other: 'CustomSet') -> bool: + def isdisjoint(self, other: "CustomSet") -> bool: pass - def __eq__(self, other: 'CustomSet') -> bool: + def __eq__(self, other: "CustomSet") -> bool: pass def add(self, element) -> None: pass - def intersection(self, other: 'CustomSet') -> 'CustomSet': + def intersection(self, other: "CustomSet") -> "CustomSet": pass - def __sub__(self, other: 'CustomSet') -> 'CustomSet': + def __sub__(self, other: "CustomSet") -> "CustomSet": pass - def __add__(self, other: 'CustomSet') -> 'CustomSet': + def __add__(self, other: "CustomSet") -> "CustomSet": pass diff --git a/evaluator/datasets/polyglot_py/darts/solution.py b/evaluator/datasets/polyglot_py/darts/solution.py index eed150cea..7086b8dd6 100644 --- a/evaluator/datasets/polyglot_py/darts/solution.py +++ b/evaluator/datasets/polyglot_py/darts/solution.py @@ -1,5 +1,6 @@ from math import sqrt + # X, and Y variable names against [pylint]: C0104, but is the same as the stub, advise not to change this. def score(x, y): dart_location = sqrt(x * x + y * y) diff --git a/evaluator/datasets/polyglot_py/diamond/solution.py b/evaluator/datasets/polyglot_py/diamond/solution.py index bd9924764..ad9014f94 100644 --- a/evaluator/datasets/polyglot_py/diamond/solution.py +++ b/evaluator/datasets/polyglot_py/diamond/solution.py @@ -8,8 +8,8 @@ def rows(letter): def make_half(lines, columns): diamond_half = [] for number in range(lines): - row = [' '] * columns + row = [" "] * columns row[lines - 1 - number] = chr(number + 65) row[lines - 1 + number] = chr(number + 65) - diamond_half.append(''.join(row)) + diamond_half.append("".join(row)) return diamond_half diff --git a/evaluator/datasets/polyglot_py/diffie-hellman/solution.py b/evaluator/datasets/polyglot_py/diffie-hellman/solution.py index 45a547bcb..d4937e1f7 100644 --- a/evaluator/datasets/polyglot_py/diffie-hellman/solution.py +++ b/evaluator/datasets/polyglot_py/diffie-hellman/solution.py @@ -2,7 +2,7 @@ def private_key(p): - return random.randint(2, p-1) + return random.randint(2, p - 1) def public_key(p, g, private): diff --git a/evaluator/datasets/polyglot_py/dnd-character/main.py b/evaluator/datasets/polyglot_py/dnd-character/main.py index 7435769b8..04c9e808e 100644 --- a/evaluator/datasets/polyglot_py/dnd-character/main.py +++ b/evaluator/datasets/polyglot_py/dnd-character/main.py @@ -2,5 +2,6 @@ class Character: def __init__(self): pass + def modifier(value: int) -> int: pass diff --git a/evaluator/datasets/polyglot_py/dnd-character/solution.py b/evaluator/datasets/polyglot_py/dnd-character/solution.py index fad3dfe17..175e134d3 100644 --- a/evaluator/datasets/polyglot_py/dnd-character/solution.py +++ b/evaluator/datasets/polyglot_py/dnd-character/solution.py @@ -1,5 +1,5 @@ -import random import math +import random class Character: @@ -18,4 +18,4 @@ def ability(self): def modifier(value): - return math.floor((value-10)/2) + return math.floor((value - 10) / 2) diff --git a/evaluator/datasets/polyglot_py/dominoes/solution.py b/evaluator/datasets/polyglot_py/dominoes/solution.py index 2490525ad..604b851a5 100644 --- a/evaluator/datasets/polyglot_py/dominoes/solution.py +++ b/evaluator/datasets/polyglot_py/dominoes/solution.py @@ -1,5 +1,5 @@ -from itertools import permutations from functools import reduce +from itertools import permutations def swap(item_1, item_2): diff --git a/evaluator/datasets/polyglot_py/dominoes/tests.py b/evaluator/datasets/polyglot_py/dominoes/tests.py index 08eefbcbf..aa6a4cb63 100644 --- a/evaluator/datasets/polyglot_py/dominoes/tests.py +++ b/evaluator/datasets/polyglot_py/dominoes/tests.py @@ -91,30 +91,19 @@ def normalize_dominoes(self, dominoes): return list(sorted(tuple(sorted(domino)) for domino in dominoes)) def assert_same_dominoes(self, input_dominoes, output_chain): - msg = ( - "Dominoes used in the output must be the same " - "as the ones given in the input" - ) + msg = "Dominoes used in the output must be the same as the ones given in the input" input_normal = self.normalize_dominoes(input_dominoes) output_normal = self.normalize_dominoes(output_chain) self.assertEqual(input_normal, output_normal, msg) def assert_consecutive_dominoes_match(self, output_chain): for i in range(len(output_chain) - 1): - msg = ( - "In chain {}, right end of domino {} ({}) " - "and left end of domino {} ({}) must match" - ) - msg = msg.format( - output_chain, i, output_chain[i], i + 1, output_chain[i + 1] - ) + msg = "In chain {}, right end of domino {} ({}) and left end of domino {} ({}) must match" + msg = msg.format(output_chain, i, output_chain[i], i + 1, output_chain[i + 1]) self.assertEqual(output_chain[i][1], output_chain[i + 1][0], msg) def assert_dominoes_at_ends_match(self, output_chain): - msg = ( - "In chain {}, left end of first domino ({}) and " - "right end of last domino ({}) must match" - ) + msg = "In chain {}, left end of first domino ({}) and right end of last domino ({}) must match" msg = msg.format(output_chain, output_chain[0], output_chain[-1]) self.assertEqual(output_chain[0][0], output_chain[-1][1], msg) diff --git a/evaluator/datasets/polyglot_py/dot-dsl/main.py b/evaluator/datasets/polyglot_py/dot-dsl/main.py index bf9c87900..308a43a14 100644 --- a/evaluator/datasets/polyglot_py/dot-dsl/main.py +++ b/evaluator/datasets/polyglot_py/dot-dsl/main.py @@ -17,9 +17,7 @@ def __init__(self, src: str, dst: str, attrs: dict): self.attrs = attrs def __eq__(self, other): - return (self.src == other.src and - self.dst == other.dst and - self.attrs == other.attrs) + return self.src == other.src and self.dst == other.dst and self.attrs == other.attrs class Graph: diff --git a/evaluator/datasets/polyglot_py/dot-dsl/solution.py b/evaluator/datasets/polyglot_py/dot-dsl/solution.py index e0a447cdf..0fe131e71 100644 --- a/evaluator/datasets/polyglot_py/dot-dsl/solution.py +++ b/evaluator/datasets/polyglot_py/dot-dsl/solution.py @@ -17,9 +17,7 @@ def __init__(self, src, dst, attrs): self.attrs = attrs def __eq__(self, other): - return (self.src == other.src and - self.dst == other.dst and - self.attrs == other.attrs) + return self.src == other.src and self.dst == other.dst and self.attrs == other.attrs class Graph: @@ -32,24 +30,24 @@ def __init__(self, data=None): data = [] if not isinstance(data, list): - raise TypeError('Graph data malformed') + raise TypeError("Graph data malformed") for item in data: if len(item) < 1: - raise TypeError('Graph item malformed') + raise TypeError("Graph item malformed") type_ = item[0] if type_ == ATTR: if len(item) != 3: - raise TypeError('Graph item malformed') + raise TypeError("Graph item malformed") self.attrs[item[1]] = item[2] elif type_ == NODE: if len(item) != 3: - raise TypeError('Graph item malformed') + raise TypeError("Graph item malformed") self.nodes.append(Node(item[1], item[2])) elif type_ == EDGE: if len(item) != 4: - raise TypeError('Graph item malformed') + raise TypeError("Graph item malformed") self.edges.append(Edge(item[1], item[2], item[3])) else: - raise ValueError('Unknown item') + raise ValueError("Unknown item") diff --git a/evaluator/datasets/polyglot_py/dot-dsl/tests.py b/evaluator/datasets/polyglot_py/dot-dsl/tests.py index b448da3ea..a94d968be 100644 --- a/evaluator/datasets/polyglot_py/dot-dsl/tests.py +++ b/evaluator/datasets/polyglot_py/dot-dsl/tests.py @@ -1,6 +1,6 @@ import unittest -from main import Graph, Node, Edge, NODE, EDGE, ATTR +from main import ATTR, EDGE, NODE, Edge, Graph, Node class DotDslTest(unittest.TestCase): @@ -12,63 +12,50 @@ def test_empty_graph(self): self.assertEqual(g.attrs, {}) def test_graph_with_one_node(self): - g = Graph([ - (NODE, "a", {}) - ]) + g = Graph([(NODE, "a", {})]) self.assertEqual(g.nodes, [Node("a", {})]) self.assertEqual(g.edges, []) self.assertEqual(g.attrs, {}) def test_graph_with_one_node_with_keywords(self): - g = Graph([ - (NODE, "a", {"color": "green"}) - ]) + g = Graph([(NODE, "a", {"color": "green"})]) self.assertEqual(g.nodes, [Node("a", {"color": "green"})]) self.assertEqual(g.edges, []) self.assertEqual(g.attrs, {}) def test_graph_with_one_edge(self): - g = Graph([ - (EDGE, "a", "b", {}) - ]) + g = Graph([(EDGE, "a", "b", {})]) self.assertEqual(g.nodes, []) self.assertEqual(g.edges, [Edge("a", "b", {})]) self.assertEqual(g.attrs, {}) def test_graph_with_one_attribute(self): - g = Graph([ - (ATTR, "foo", "1") - ]) + g = Graph([(ATTR, "foo", "1")]) self.assertEqual(g.nodes, []) self.assertEqual(g.edges, []) self.assertEqual(g.attrs, {"foo": "1"}) def test_graph_with_attributes(self): - g = Graph([ - (ATTR, "foo", "1"), - (ATTR, "title", "Testing Attrs"), - (NODE, "a", {"color": "green"}), - (NODE, "c", {}), - (NODE, "b", {"label": "Beta!"}), - (EDGE, "b", "c", {}), - (EDGE, "a", "b", {"color": "blue"}), - (ATTR, "bar", "true") - ]) - - self.assertEqual(g.nodes, [Node("a", {"color": "green"}), - Node("c", {}), - Node("b", {"label": "Beta!"})]) - self.assertEqual(g.edges, [Edge("b", "c", {}), - Edge("a", "b", {"color": "blue"})]) - self.assertEqual(g.attrs, { - "foo": "1", - "title": "Testing Attrs", - "bar": "true" - }) + g = Graph( + [ + (ATTR, "foo", "1"), + (ATTR, "title", "Testing Attrs"), + (NODE, "a", {"color": "green"}), + (NODE, "c", {}), + (NODE, "b", {"label": "Beta!"}), + (EDGE, "b", "c", {}), + (EDGE, "a", "b", {"color": "blue"}), + (ATTR, "bar", "true"), + ] + ) + + self.assertEqual(g.nodes, [Node("a", {"color": "green"}), Node("c", {}), Node("b", {"label": "Beta!"})]) + self.assertEqual(g.edges, [Edge("b", "c", {}), Edge("a", "b", {"color": "blue"})]) + self.assertEqual(g.attrs, {"foo": "1", "title": "Testing Attrs", "bar": "true"}) def test_malformed_graph(self): with self.assertRaises(TypeError) as err: @@ -87,27 +74,23 @@ def test_malformed_graph_item(self): self.assertEqual(type(err.exception), TypeError) self.assertEqual(err.exception.args[0], "Graph item malformed") - with self.assertRaises(TypeError) as err: - Graph([(ATTR, )]) + Graph([(ATTR,)]) self.assertEqual(type(err.exception), TypeError) self.assertEqual(err.exception.args[0], "Graph item malformed") - def test_malformed_attr(self): with self.assertRaises(TypeError) as err: Graph([(ATTR, 1, 2, 3)]) self.assertEqual(type(err.exception), TypeError) self.assertEqual(err.exception.args[0], "Graph item malformed") - def test_malformed_node(self): with self.assertRaises(TypeError) as err: Graph([(NODE, 1, 2, 3)]) self.assertEqual(type(err.exception), TypeError) self.assertEqual(err.exception.args[0], "Graph item malformed") - def test_malformed_EDGE(self): with self.assertRaises(TypeError) as err: Graph([(EDGE, 1, 2)]) diff --git a/evaluator/datasets/polyglot_py/error-handling/solution.py b/evaluator/datasets/polyglot_py/error-handling/solution.py index 1dba190d4..f4b7fd9a2 100644 --- a/evaluator/datasets/polyglot_py/error-handling/solution.py +++ b/evaluator/datasets/polyglot_py/error-handling/solution.py @@ -1,5 +1,5 @@ def handle_error_by_throwing_exception(): - raise Exception('Meaningful message describing the source of the error') + raise Exception("Meaningful message describing the source of the error") def handle_error_by_returning_none(input_data): diff --git a/evaluator/datasets/polyglot_py/error-handling/tests.py b/evaluator/datasets/polyglot_py/error-handling/tests.py index c26dcce44..3ad504d81 100644 --- a/evaluator/datasets/polyglot_py/error-handling/tests.py +++ b/evaluator/datasets/polyglot_py/error-handling/tests.py @@ -2,52 +2,44 @@ import main as er + class ErrorHandlingTest(unittest.TestCase): def test_throw_exception(self): with self.assertRaisesWithMessage(Exception): er.handle_error_by_throwing_exception() def test_return_none(self): - self.assertEqual(er.handle_error_by_returning_none('1'), 1, - 'Result of valid input should not be None') - self.assertIsNone(er.handle_error_by_returning_none('a'), - 'Result of invalid input should be None') + self.assertEqual(er.handle_error_by_returning_none("1"), 1, "Result of valid input should not be None") + self.assertIsNone(er.handle_error_by_returning_none("a"), "Result of invalid input should be None") def test_return_tuple(self): - successful_result, result = er.handle_error_by_returning_tuple('1') - self.assertIs(successful_result, True, - 'Valid input should be successful') - self.assertEqual(result, 1, 'Result of valid input should not be None') + successful_result, result = er.handle_error_by_returning_tuple("1") + self.assertIs(successful_result, True, "Valid input should be successful") + self.assertEqual(result, 1, "Result of valid input should not be None") - failure_result, result = er.handle_error_by_returning_tuple('a') - self.assertIs(failure_result, False, - 'Invalid input should not be successful') + failure_result, result = er.handle_error_by_returning_tuple("a") + self.assertIs(failure_result, False, "Invalid input should not be successful") def test_filelike_objects_are_closed_on_exception(self): filelike_object = FileLike(fail_something=True) with self.assertRaisesWithMessage(Exception): er.filelike_objects_are_closed_on_exception(filelike_object) - self.assertIs(filelike_object.is_open, False, - 'filelike_object should be closed') - self.assertIs(filelike_object.was_open, True, - 'filelike_object should have been opened') - self.assertIs(filelike_object.did_something, True, - 'filelike_object should call do_something()') + self.assertIs(filelike_object.is_open, False, "filelike_object should be closed") + self.assertIs(filelike_object.was_open, True, "filelike_object should have been opened") + self.assertIs(filelike_object.did_something, True, "filelike_object should call do_something()") def test_filelike_objects_are_closed_without_exception(self): filelike_object = FileLike(fail_something=False) er.filelike_objects_are_closed_on_exception(filelike_object) - self.assertIs(filelike_object.is_open, False, - 'filelike_object should be closed') - self.assertIs(filelike_object.was_open, True, - 'filelike_object should have been opened') - self.assertIs(filelike_object.did_something, True, - 'filelike_object should call do_something()') + self.assertIs(filelike_object.is_open, False, "filelike_object should be closed") + self.assertIs(filelike_object.was_open, True, "filelike_object should have been opened") + self.assertIs(filelike_object.did_something, True, "filelike_object should call do_something()") # Utility functions def assertRaisesWithMessage(self, exception): return self.assertRaisesRegex(exception, r".+") + class FileLike: def __init__(self, fail_something=True): self.is_open = False @@ -75,5 +67,6 @@ def do_something(self): if self.fail_something: raise Exception("Failed while doing something") -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/etl/solution.py b/evaluator/datasets/polyglot_py/etl/solution.py index 0e2b65824..4c9df1da8 100644 --- a/evaluator/datasets/polyglot_py/etl/solution.py +++ b/evaluator/datasets/polyglot_py/etl/solution.py @@ -1,6 +1,2 @@ def transform(legacy_data): - return { - letter.lower(): points - for points, letters in legacy_data.items() - for letter in letters - } + return {letter.lower(): points for points, letters in legacy_data.items() for letter in letters} diff --git a/evaluator/datasets/polyglot_py/flower-field/solution.py b/evaluator/datasets/polyglot_py/flower-field/solution.py index e3ae009cc..ed550d208 100644 --- a/evaluator/datasets/polyglot_py/flower-field/solution.py +++ b/evaluator/datasets/polyglot_py/flower-field/solution.py @@ -8,33 +8,33 @@ def annotate(garden): for index1 in range(col_len): for index2 in range(row_len): - if board[index1][index2] != ' ': + if board[index1][index2] != " ": continue low = max(index2 - 1, 0) high = min(index2 + 2, row_len + 2) - counts = garden[index1][low:high].count('*') + counts = garden[index1][low:high].count("*") if index1 > 0: - counts += garden[index1 - 1][low:high].count('*') + counts += garden[index1 - 1][low:high].count("*") if index1 < col_len - 1: - counts += garden[index1 + 1][low:high].count('*') + counts += garden[index1 + 1][low:high].count("*") if counts == 0: continue board[index1][index2] = str(counts) - return [''.join(row) for row in board] + return ["".join(row) for row in board] def verify_board(garden): # Rows with different lengths row_len = len(garden[0]) if not all(len(row) == row_len for row in garden): - raise ValueError('The board is invalid with current input.') + raise ValueError("The board is invalid with current input.") # Unknown character in board character_set = set() for row in garden: character_set.update(row) - if character_set - set(' *'): - raise ValueError('The board is invalid with current input.') + if character_set - set(" *"): + raise ValueError("The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py/flower-field/tests.py b/evaluator/datasets/polyglot_py/flower-field/tests.py index f59493969..12df7e013 100644 --- a/evaluator/datasets/polyglot_py/flower-field/tests.py +++ b/evaluator/datasets/polyglot_py/flower-field/tests.py @@ -63,14 +63,10 @@ def test_different_len(self): with self.assertRaises(ValueError) as err: annotate([" ", "* ", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") def test_invalid_char(self): with self.assertRaises(ValueError) as err: annotate(["X * "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py/food-chain/solution.py b/evaluator/datasets/polyglot_py/food-chain/solution.py index 075f83faf..6c2e486f7 100644 --- a/evaluator/datasets/polyglot_py/food-chain/solution.py +++ b/evaluator/datasets/polyglot_py/food-chain/solution.py @@ -1,62 +1,64 @@ def get_song(): - animals = ['fly', 'spider', 'bird', 'cat', 'dog', 'goat', 'cow', 'horse'] - - phrases = [' wriggled and jiggled and tickled inside her.', - 'How absurd to swallow a bird!', - 'Imagine that, to swallow a cat!', - 'What a hog, to swallow a dog!', - 'Just opened her throat and swallowed a goat!', - "I don't know how she swallowed a cow!", - "She's dead, of course!"] - - old_lady = 'I know an old lady who swallowed a ' - swallowed = 'She swallowed the to catch the ' + animals = ["fly", "spider", "bird", "cat", "dog", "goat", "cow", "horse"] + + phrases = [ + " wriggled and jiggled and tickled inside her.", + "How absurd to swallow a bird!", + "Imagine that, to swallow a cat!", + "What a hog, to swallow a dog!", + "Just opened her throat and swallowed a goat!", + "I don't know how she swallowed a cow!", + "She's dead, of course!", + ] + + old_lady = "I know an old lady who swallowed a " + swallowed = "She swallowed the to catch the " die = "I don't know why she swallowed the fly. Perhaps she'll die." - song = '' - verse = '' - chain = '' + song = "" + verse = "" + chain = "" for number, animal in enumerate(animals): - verse = old_lady + animal + '.\n' + verse = old_lady + animal + ".\n" if number == 7: verse += phrases[6] else: if number == 0: - chain = swallowed + animal + '.\n' + chain = swallowed + animal + ".\n" elif number == 1: - verse += 'It' + phrases[0] + '\n' - chain = chain.replace('', animal) + verse += "It" + phrases[0] + "\n" + chain = chain.replace("", animal) verse += chain - chain = swallowed + animal + ' that' + phrases[0] + '\n' + chain + chain = swallowed + animal + " that" + phrases[0] + "\n" + chain else: - verse += phrases[number-1] + '\n' - chain = chain.replace('', animal) + verse += phrases[number - 1] + "\n" + chain = chain.replace("", animal) verse += chain - chain = swallowed + animal + '.\n' + chain + chain = swallowed + animal + ".\n" + chain - verse += die + '\n' + verse += die + "\n" - verse += '\n' + verse += "\n" song += verse return song def verses(letter): - return letter.replace('die.', 'die.slice').split('slice') + return letter.replace("die.", "die.slice").split("slice") def recite(start_verse, end_verse): - generated = [verse.strip().split('\n') for verse in verses(get_song())] + generated = [verse.strip().split("\n") for verse in verses(get_song())] if start_verse == end_verse: return generated[start_verse - 1] else: result = [] for idx in range(start_verse - 1, end_verse): - result += generated[idx] + [''] + result += generated[idx] + [""] # Pop out the last empty string result.pop() diff --git a/evaluator/datasets/polyglot_py/forth/solution.py b/evaluator/datasets/polyglot_py/forth/solution.py index 5e499acf5..71155eaa9 100644 --- a/evaluator/datasets/polyglot_py/forth/solution.py +++ b/evaluator/datasets/polyglot_py/forth/solution.py @@ -1,7 +1,8 @@ class StackUnderflowError(Exception): """Exception raised when Stack is not full. - message: explanation of the error. + message: explanation of the error. """ + def __init__(self, message): self.message = message @@ -18,18 +19,14 @@ def evaluate(input_data): if not input_data: return [] defines = {} - while input_data[0][:1] == ':': + while input_data[0][:1] == ":": values = input_data.pop(0).split() values.pop() values.pop(0) key = values.pop(0).lower() if is_integer(key): - raise ValueError('illegal operation') - defines[key] = [ - idx - for vivaldi in values - for idx in defines.get(vivaldi, [vivaldi]) - ] + raise ValueError("illegal operation") + defines[key] = [idx for vivaldi in values for idx in defines.get(vivaldi, [vivaldi])] stack = [] input_data = input_data[-1].split() while any(input_data): @@ -39,28 +36,28 @@ def evaluate(input_data): stack.append(int(word)) elif word in defines: input_data = defines[word] + input_data - elif word == '+': + elif word == "+": stack.append(stack.pop() + stack.pop()) - elif word == '-': + elif word == "-": stack.append(-stack.pop() + stack.pop()) - elif word == '*': + elif word == "*": stack.append(stack.pop() * stack.pop()) - elif word == '/': + elif word == "/": divisor = stack.pop() if divisor == 0: - raise ZeroDivisionError('divide by zero') + raise ZeroDivisionError("divide by zero") stack.append(int(stack.pop() / divisor)) - elif word == 'dup': + elif word == "dup": stack.append(stack[-1]) - elif word == 'drop': + elif word == "drop": stack.pop() - elif word == 'swap': + elif word == "swap": stack.append(stack[-2]) del stack[-3] - elif word == 'over': + elif word == "over": stack.append(stack[-2]) else: - raise ValueError('undefined operation') + raise ValueError("undefined operation") except IndexError as error: - raise StackUnderflowError('Insufficient number of items in stack') from error + raise StackUnderflowError("Insufficient number of items in stack") from error return stack diff --git a/evaluator/datasets/polyglot_py/forth/tests.py b/evaluator/datasets/polyglot_py/forth/tests.py index 3a528b87f..6df225079 100644 --- a/evaluator/datasets/polyglot_py/forth/tests.py +++ b/evaluator/datasets/polyglot_py/forth/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - evaluate, StackUnderflowError, + evaluate, ) @@ -24,17 +24,13 @@ def test_addition_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["+"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_addition_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 +"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_addition_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 2 3 +"]), [1, 5]) @@ -46,17 +42,13 @@ def test_subtraction_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["-"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_subtraction_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 -"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_subtraction_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 12 3 -"]), [1, 9]) @@ -68,17 +60,13 @@ def test_multiplication_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["*"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_multiplication_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 *"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_multiplication_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 2 3 *"]), [1, 6]) @@ -100,17 +88,13 @@ def test_division_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["/"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_division_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 /"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_division_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 12 3 /"]), [1, 4]) @@ -137,9 +121,7 @@ def test_dup_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["dup"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_drop_removes_the_top_value_on_the_stack_if_it_is_the_only_one(self): self.assertEqual(evaluate(["1 drop"]), []) @@ -151,9 +133,7 @@ def test_drop_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["drop"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_swap_swaps_the_top_two_values_on_the_stack_if_they_are_the_only_ones(self): self.assertEqual(evaluate(["1 2 swap"]), [2, 1]) @@ -167,17 +147,13 @@ def test_swap_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["swap"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_swap_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 swap"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_over_copies_the_second_element_if_there_are_only_two(self): self.assertEqual(evaluate(["1 2 over"]), [1, 2, 1]) @@ -189,17 +165,13 @@ def test_over_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["over"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_over_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 over"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_user_defined_words_can_consist_of_built_in_words(self): self.assertEqual(evaluate([": dup-twice dup dup ;", "1 dup-twice"]), [1, 1, 1]) @@ -208,9 +180,7 @@ def test_user_defined_words_execute_in_the_right_order(self): self.assertEqual(evaluate([": countup 1 2 3 ;", "countup"]), [1, 2, 3]) def test_user_defined_words_can_override_other_user_defined_words(self): - self.assertEqual( - evaluate([": foo dup ;", ": foo dup dup ;", "1 foo"]), [1, 1, 1] - ) + self.assertEqual(evaluate([": foo dup ;", ": foo dup dup ;", "1 foo"]), [1, 1, 1]) def test_user_defined_words_can_override_built_in_words(self): self.assertEqual(evaluate([": swap dup ;", "1 swap"]), [1, 1]) @@ -219,9 +189,7 @@ def test_user_defined_words_can_override_built_in_operators(self): self.assertEqual(evaluate([": + * ;", "3 4 +"]), [12]) def test_user_defined_words_can_use_different_words_with_the_same_name(self): - self.assertEqual( - evaluate([": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"]), [5, 6] - ) + self.assertEqual(evaluate([": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"]), [5, 6]) def test_user_defined_words_can_define_word_that_uses_word_with_the_same_name(self): self.assertEqual(evaluate([": foo 10 ;", ": foo foo 1 + ;", "foo"]), [11]) diff --git a/evaluator/datasets/polyglot_py/gigasecond/main.py b/evaluator/datasets/polyglot_py/gigasecond/main.py index 775c4dd7a..3dcf347b3 100644 --- a/evaluator/datasets/polyglot_py/gigasecond/main.py +++ b/evaluator/datasets/polyglot_py/gigasecond/main.py @@ -1,4 +1,5 @@ from datetime import datetime + def add(moment: datetime) -> datetime: pass diff --git a/evaluator/datasets/polyglot_py/gigasecond/tests.py b/evaluator/datasets/polyglot_py/gigasecond/tests.py index 26c4a26d2..70afd0f3f 100644 --- a/evaluator/datasets/polyglot_py/gigasecond/tests.py +++ b/evaluator/datasets/polyglot_py/gigasecond/tests.py @@ -2,8 +2,8 @@ # https://github.com/exercism/problem-specifications/tree/main/exercises/gigasecond/canonical-data.json # File last updated on 2023-07-19 -from datetime import datetime import unittest +from datetime import datetime from main import ( add, @@ -12,26 +12,16 @@ class GigasecondTest(unittest.TestCase): def test_date_only_specification_of_time(self): - self.assertEqual( - add(datetime(2011, 4, 25, 0, 0)), datetime(2043, 1, 1, 1, 46, 40) - ) + self.assertEqual(add(datetime(2011, 4, 25, 0, 0)), datetime(2043, 1, 1, 1, 46, 40)) def test_second_test_for_date_only_specification_of_time(self): - self.assertEqual( - add(datetime(1977, 6, 13, 0, 0)), datetime(2009, 2, 19, 1, 46, 40) - ) + self.assertEqual(add(datetime(1977, 6, 13, 0, 0)), datetime(2009, 2, 19, 1, 46, 40)) def test_third_test_for_date_only_specification_of_time(self): - self.assertEqual( - add(datetime(1959, 7, 19, 0, 0)), datetime(1991, 3, 27, 1, 46, 40) - ) + self.assertEqual(add(datetime(1959, 7, 19, 0, 0)), datetime(1991, 3, 27, 1, 46, 40)) def test_full_time_specified(self): - self.assertEqual( - add(datetime(2015, 1, 24, 22, 0)), datetime(2046, 10, 2, 23, 46, 40) - ) + self.assertEqual(add(datetime(2015, 1, 24, 22, 0)), datetime(2046, 10, 2, 23, 46, 40)) def test_full_time_with_day_roll_over(self): - self.assertEqual( - add(datetime(2015, 1, 24, 23, 59, 59)), datetime(2046, 10, 3, 1, 46, 39) - ) + self.assertEqual(add(datetime(2015, 1, 24, 23, 59, 59)), datetime(2046, 10, 3, 1, 46, 39)) diff --git a/evaluator/datasets/polyglot_py/go-counting/main.py b/evaluator/datasets/polyglot_py/go-counting/main.py index 26619c4a3..ce3e7c7e9 100644 --- a/evaluator/datasets/polyglot_py/go-counting/main.py +++ b/evaluator/datasets/polyglot_py/go-counting/main.py @@ -1,4 +1,3 @@ - class Board: """Count territories of each player in a Go game diff --git a/evaluator/datasets/polyglot_py/go-counting/solution.py b/evaluator/datasets/polyglot_py/go-counting/solution.py index c6ccd5121..cf7194dbc 100644 --- a/evaluator/datasets/polyglot_py/go-counting/solution.py +++ b/evaluator/datasets/polyglot_py/go-counting/solution.py @@ -1,7 +1,6 @@ - -BLACK = 'B' -WHITE = 'W' -NONE = '' +BLACK = "B" +WHITE = "W" +NONE = "" STONES = [BLACK, WHITE] DIRECTIONS = [(0, 1), (0, -1), (1, 0), (-1, 0)] @@ -28,10 +27,13 @@ def walk(self, width, height, visited_territory=None, visited_coords=None, visit return (visited_territory, visited_stones + [stone]) else: # s is empty for direction in DIRECTIONS: - visited = self.walk(width + direction[0], height + direction[1], - visited_territory + [(width, height)], - visited_coords + [(width, height)], - visited_stones) + visited = self.walk( + width + direction[0], + height + direction[1], + visited_territory + [(width, height)], + visited_coords + [(width, height)], + visited_stones, + ) visited_territory = visited[0] visited_stones = visited[1] @@ -39,7 +41,7 @@ def walk(self, width, height, visited_territory=None, visited_coords=None, visit def territory(self, x, y): if not self.valid(x, y): - raise ValueError('Invalid coordinate') + raise ValueError("Invalid coordinate") if self.board[y][x] in STONES: return (NONE, set()) @@ -52,11 +54,11 @@ def territory(self, x, y): def territories(self): owners = STONES + [NONE] - result = {owner:set() for owner in owners} + result = {owner: set() for owner in owners} visited = set() for row in range(self.height): for column in range(self.width): - if not (column, row) in visited: + if (column, row) not in visited: owner, owned_territories = self.territory(column, row) result[owner].update(owned_territories) visited.update(owned_territories) diff --git a/evaluator/datasets/polyglot_py/grade-school/solution.py b/evaluator/datasets/polyglot_py/grade-school/solution.py index fc974919e..501168218 100644 --- a/evaluator/datasets/polyglot_py/grade-school/solution.py +++ b/evaluator/datasets/polyglot_py/grade-school/solution.py @@ -19,7 +19,6 @@ def add_student(self, name, grade): else: self.add.append(False) - def roster(self, grade=0): grades_roster = defaultdict(list) diff --git a/evaluator/datasets/polyglot_py/grains/solution.py b/evaluator/datasets/polyglot_py/grains/solution.py index 8610d32b5..7b5aaa4ba 100644 --- a/evaluator/datasets/polyglot_py/grains/solution.py +++ b/evaluator/datasets/polyglot_py/grains/solution.py @@ -1,13 +1,13 @@ def square(number): if number == 0: - raise ValueError('square must be between 1 and 64') + raise ValueError("square must be between 1 and 64") elif number < 0: - raise ValueError('square must be between 1 and 64') + raise ValueError("square must be between 1 and 64") elif number > 64: - raise ValueError('square must be between 1 and 64') + raise ValueError("square must be between 1 and 64") return 2 ** (number - 1) def total(): - return (2 ** 64) - 1 + return (2**64) - 1 diff --git a/evaluator/datasets/polyglot_py/grep/solution.py b/evaluator/datasets/polyglot_py/grep/solution.py index e0ffbfe2d..f2ee5348e 100644 --- a/evaluator/datasets/polyglot_py/grep/solution.py +++ b/evaluator/datasets/polyglot_py/grep/solution.py @@ -1,24 +1,24 @@ def matches(line, pattern, flags): - if '-i' in flags: # case-insensitive + if "-i" in flags: # case-insensitive line = line.lower() pattern = pattern.lower() - if '-x' in flags: # match entire lines + if "-x" in flags: # match entire lines if len(pattern) != len(line.rstrip()): - return '-v' in flags + return "-v" in flags - if '-v' in flags: # invert matching + if "-v" in flags: # invert matching return pattern not in line return pattern in line def format_files(matched_lines): - result = '' + result = "" for file_name, _, _ in matched_lines: if file_name not in result: - result += file_name + '\n' + result += file_name + "\n" return result @@ -27,31 +27,31 @@ def format_lines(matched_lines, flags, files): result = [] for file_name, line_number, line in matched_lines: - line_result = '' + line_result = "" if len(files) > 1: - line_result += file_name + ':' + line_result += file_name + ":" - if '-n' in flags: - line_result += str(line_number) + ':' + if "-n" in flags: + line_result += str(line_number) + ":" line_result += line result.append(line_result) - return ''.join(result) + return "".join(result) def grep(pattern, flags, files): matched_lines = [] for file_name in files: - with open(file_name, encoding='utf-8') as f: + with open(file_name, encoding="utf-8") as f: for line_number, line in enumerate(f.readlines(), start=1): if matches(line, pattern, flags): matched_lines.append((file_name, line_number, line)) - if '-l' in flags: + if "-l" in flags: return format_files(matched_lines) return format_lines(matched_lines, flags, files) diff --git a/evaluator/datasets/polyglot_py/grep/tests.py b/evaluator/datasets/polyglot_py/grep/tests.py index 3f62f6ba2..68d8dc34c 100644 --- a/evaluator/datasets/polyglot_py/grep/tests.py +++ b/evaluator/datasets/polyglot_py/grep/tests.py @@ -4,11 +4,11 @@ import io import unittest +from unittest import mock from main import ( grep, ) -from unittest import mock FILE_TEXT = { "iliad.txt": """Achilles sing, O Goddess! Peleus' son; @@ -42,9 +42,7 @@ def open_mock(fname, *args, **kwargs): try: return io.StringIO(FILE_TEXT[fname]) except KeyError: - raise RuntimeError( - "Expected one of {0!r}: got {1!r}".format(list(FILE_TEXT.keys()), fname) - ) + raise RuntimeError("Expected one of {0!r}: got {1!r}".format(list(FILE_TEXT.keys()), fname)) @mock.patch("main.open", name="open", side_effect=open_mock, create=True) @@ -52,9 +50,7 @@ def open_mock(fname, *args, **kwargs): class GrepTest(unittest.TestCase): # Test grepping a single file def test_one_file_one_match_no_flags(self, mock_file, mock_open): - self.assertMultiLineEqual( - grep("Agamemnon", "", ["iliad.txt"]), "Of Atreus, Agamemnon, King of men.\n" - ) + self.assertMultiLineEqual(grep("Agamemnon", "", ["iliad.txt"]), "Of Atreus, Agamemnon, King of men.\n") def test_one_file_one_match_print_line_numbers_flag(self, mock_file, mock_open): self.assertMultiLineEqual( @@ -69,15 +65,11 @@ def test_one_file_one_match_case_insensitive_flag(self, mock_file, mock_open): ) def test_one_file_one_match_print_file_names_flag(self, mock_file, mock_open): - self.assertMultiLineEqual( - grep("Forbidden", "-l", ["paradise-lost.txt"]), "paradise-lost.txt\n" - ) + self.assertMultiLineEqual(grep("Forbidden", "-l", ["paradise-lost.txt"]), "paradise-lost.txt\n") def test_one_file_one_match_match_entire_lines_flag(self, mock_file, mock_open): self.assertMultiLineEqual( - grep( - "With loss of Eden, till one greater Man", "-x", ["paradise-lost.txt"] - ), + grep("With loss of Eden, till one greater Man", "-x", ["paradise-lost.txt"]), "With loss of Eden, till one greater Man\n", ) @@ -95,9 +87,7 @@ def test_one_file_several_matches_no_flags(self, mock_file, mock_open): "The worst that may befall me in this case,\n", ) - def test_one_file_several_matches_print_line_numbers_flag( - self, mock_file, mock_open - ): + def test_one_file_several_matches_print_line_numbers_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep("may", "-n", ["midsummer-night.txt"]), "3:Nor how it may concern my modesty,\n" @@ -105,16 +95,13 @@ def test_one_file_several_matches_print_line_numbers_flag( "6:The worst that may befall me in this case,\n", ) - def test_one_file_several_matches_match_entire_lines_flag( - self, mock_file, mock_open - ): + def test_one_file_several_matches_match_entire_lines_flag(self, mock_file, mock_open): self.assertMultiLineEqual(grep("may", "-x", ["midsummer-night.txt"]), "") def test_one_file_several_matches_case_insensitive_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep("ACHILLES", "-i", ["iliad.txt"]), - "Achilles sing, O Goddess! Peleus' son;\n" - "The noble Chief Achilles from the son\n", + "Achilles sing, O Goddess! Peleus' son;\nThe noble Chief Achilles from the son\n", ) def test_one_file_several_matches_inverted_flag(self, mock_file, mock_open): @@ -130,14 +117,10 @@ def test_one_file_several_matches_inverted_flag(self, mock_file, mock_open): def test_one_file_no_matches_various_flags(self, mock_file, mock_open): self.assertMultiLineEqual(grep("Gandalf", "-n -l -x -i", ["iliad.txt"]), "") - def test_one_file_one_match_file_flag_takes_precedence_over_line_flag( - self, mock_file, mock_open - ): + def test_one_file_one_match_file_flag_takes_precedence_over_line_flag(self, mock_file, mock_open): self.assertMultiLineEqual(grep("ten", "-n -l", ["iliad.txt"]), "iliad.txt\n") - def test_one_file_several_matches_inverted_and_match_entire_lines_flags( - self, mock_file, mock_open - ): + def test_one_file_several_matches_inverted_and_match_entire_lines_flags(self, mock_file, mock_open): self.assertMultiLineEqual( grep("Illustrious into Ades premature,", "-x -v", ["iliad.txt"]), "Achilles sing, O Goddess! Peleus' son;\n" @@ -169,13 +152,9 @@ def test_multiple_files_several_matches_no_flags(self, mock_file, mock_open): "midsummer-night.txt:The worst that may befall me in this case,\n", ) - def test_multiple_files_several_matches_print_line_numbers_flag( - self, mock_file, mock_open - ): + def test_multiple_files_several_matches_print_line_numbers_flag(self, mock_file, mock_open): self.assertMultiLineEqual( - grep( - "that", "-n", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"] - ), + grep("that", "-n", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"]), "midsummer-night.txt:5:But I beseech your grace that I may know\n" "midsummer-night.txt:6:The worst that may befall me in this case,\n" "paradise-lost.txt:2:Of that Forbidden Tree, whose mortal tast\n" @@ -184,15 +163,11 @@ def test_multiple_files_several_matches_print_line_numbers_flag( def test_multiple_files_one_match_print_file_names_flag(self, mock_file, mock_open): self.assertMultiLineEqual( - grep( - "who", "-l", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"] - ), - "iliad.txt\n" "paradise-lost.txt\n", + grep("who", "-l", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"]), + "iliad.txt\nparadise-lost.txt\n", ) - def test_multiple_files_several_matches_case_insensitive_flag( - self, mock_file, mock_open - ): + def test_multiple_files_several_matches_case_insensitive_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep("TO", "-i", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"]), "iliad.txt:Caused to Achaia's host, sent many a soul\n" @@ -215,9 +190,7 @@ def test_multiple_files_several_matches_inverted_flag(self, mock_file, mock_open "midsummer-night.txt:If I refuse to wed Demetrius.\n", ) - def test_multiple_files_one_match_match_entire_lines_flag( - self, mock_file, mock_open - ): + def test_multiple_files_one_match_match_entire_lines_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep( "But I beseech your grace that I may know", @@ -256,12 +229,10 @@ def test_multiple_files_several_matches_file_flag_takes_precedence_over_line_num "-n -l", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], ), - "iliad.txt\n" "paradise-lost.txt\n", + "iliad.txt\nparadise-lost.txt\n", ) - def test_multiple_files_several_matches_inverted_and_match_entire_lines_flags( - self, mock_file, mock_open - ): + def test_multiple_files_several_matches_inverted_and_match_entire_lines_flags(self, mock_file, mock_open): self.assertMultiLineEqual( grep( "Illustrious into Ades premature,", diff --git a/evaluator/datasets/polyglot_py/hamming/solution.py b/evaluator/datasets/polyglot_py/hamming/solution.py index d6a3848ef..943c580fa 100644 --- a/evaluator/datasets/polyglot_py/hamming/solution.py +++ b/evaluator/datasets/polyglot_py/hamming/solution.py @@ -1,5 +1,5 @@ def distance(strand_a, strand_b): if len(strand_a) != len(strand_b): - raise ValueError('Strands must be of equal length.') + raise ValueError("Strands must be of equal length.") return sum(a_part != b_part for a_part, b_part in zip(strand_a, strand_b)) diff --git a/evaluator/datasets/polyglot_py/hangman/main.py b/evaluator/datasets/polyglot_py/hangman/main.py index 5db03d51d..c216a57d0 100644 --- a/evaluator/datasets/polyglot_py/hangman/main.py +++ b/evaluator/datasets/polyglot_py/hangman/main.py @@ -1,8 +1,8 @@ # Game status categories # Change the values as you see fit -STATUS_WIN = 'win' -STATUS_LOSE = 'lose' -STATUS_ONGOING = 'ongoing' +STATUS_WIN = "win" +STATUS_LOSE = "lose" +STATUS_ONGOING = "ongoing" class Hangman: diff --git a/evaluator/datasets/polyglot_py/hangman/solution.py b/evaluator/datasets/polyglot_py/hangman/solution.py index f743656af..c4bc994ad 100644 --- a/evaluator/datasets/polyglot_py/hangman/solution.py +++ b/evaluator/datasets/polyglot_py/hangman/solution.py @@ -1,6 +1,6 @@ -STATUS_WIN = 'win' -STATUS_LOSE = 'lose' -STATUS_ONGOING = 'ongoing' +STATUS_WIN = "win" +STATUS_LOSE = "lose" +STATUS_ONGOING = "ongoing" class Hangman: @@ -8,24 +8,24 @@ def __init__(self, word): self.remaining_guesses = 10 self.status = STATUS_ONGOING self.word = word - self.masked_word = '' + self.masked_word = "" self.guesses = [] for _ in self.word: - self.masked_word += '_' + self.masked_word += "_" def guess(self, char): if self.status != STATUS_ONGOING: - raise ValueError('The game has already ended.') + raise ValueError("The game has already ended.") self.update_remaining_guesses(char) self.update_masked_word() self.update_status() def update_masked_word(self): - self.masked_word = '' + self.masked_word = "" for idx in self.word: if idx not in self.guesses: - self.masked_word += '_' + self.masked_word += "_" else: self.masked_word += idx diff --git a/evaluator/datasets/polyglot_py/hangman/tests.py b/evaluator/datasets/polyglot_py/hangman/tests.py index 1620cde61..6f11ef64f 100644 --- a/evaluator/datasets/polyglot_py/hangman/tests.py +++ b/evaluator/datasets/polyglot_py/hangman/tests.py @@ -3,95 +3,95 @@ import main from main import Hangman - # Tests adapted from csharp//hangman/HangmanTest.cs + class HangmanTests(unittest.TestCase): def test_initially_9_failures_are_allowed(self): - game = Hangman('foo') + game = Hangman("foo") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 10) def test_initially_no_letters_are_guessed(self): - game = Hangman('foo') + game = Hangman("foo") - self.assertEqual(game.get_masked_word(), '___') + self.assertEqual(game.get_masked_word(), "___") def test_after_10_failures_the_game_is_over(self): - game = Hangman('foo') + game = Hangman("foo") for i in range(10): - game.guess('x') + game.guess("x") self.assertEqual(game.get_status(), main.STATUS_LOSE) with self.assertRaises(ValueError) as err: - game.guess('x') + game.guess("x") self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "The game has already ended.") def test_feeding_a_correct_letter_removes_underscores(self): - game = Hangman('foobar') + game = Hangman("foobar") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 10) - self.assertEqual(game.get_masked_word(), '___b__') + self.assertEqual(game.get_masked_word(), "___b__") - game.guess('o') + game.guess("o") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 10) - self.assertEqual(game.get_masked_word(), '_oob__') + self.assertEqual(game.get_masked_word(), "_oob__") def test_feeding_a_correct_letter_twice_counts_as_a_failure(self): - game = Hangman('foobar') + game = Hangman("foobar") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 10) - self.assertEqual(game.get_masked_word(), '___b__') + self.assertEqual(game.get_masked_word(), "___b__") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '___b__') + self.assertEqual(game.get_masked_word(), "___b__") def test_getting_all_the_letters_right_makes_for_a_win(self): - game = Hangman('hello') + game = Hangman("hello") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '_____') + self.assertEqual(game.get_masked_word(), "_____") - game.guess('e') + game.guess("e") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '_e___') + self.assertEqual(game.get_masked_word(), "_e___") - game.guess('l') + game.guess("l") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '_ell_') + self.assertEqual(game.get_masked_word(), "_ell_") - game.guess('o') + game.guess("o") self.assertEqual(game.get_status(), main.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '_ello') + self.assertEqual(game.get_masked_word(), "_ello") - game.guess('h') + game.guess("h") self.assertEqual(game.get_status(), main.STATUS_WIN) - self.assertEqual(game.get_masked_word(), 'hello') + self.assertEqual(game.get_masked_word(), "hello") with self.assertRaises(ValueError) as err: - game.guess('x') + game.guess("x") self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "The game has already ended.") def test_winning_on_last_guess_still_counts_as_a_win(self): - game = Hangman('aaa') - for ch in 'bcdefghij': + game = Hangman("aaa") + for ch in "bcdefghij": game.guess(ch) - game.guess('a') + game.guess("a") self.assertEqual(game.remaining_guesses, 1) self.assertEqual(game.get_status(), main.STATUS_WIN) - self.assertEqual(game.get_masked_word(), 'aaa') + self.assertEqual(game.get_masked_word(), "aaa") diff --git a/evaluator/datasets/polyglot_py/hello-world/main.py b/evaluator/datasets/polyglot_py/hello-world/main.py index eb0c869b0..54e127d6f 100644 --- a/evaluator/datasets/polyglot_py/hello-world/main.py +++ b/evaluator/datasets/polyglot_py/hello-world/main.py @@ -1,2 +1,2 @@ def hello() -> str: - return 'Goodbye, Mars!' + return "Goodbye, Mars!" diff --git a/evaluator/datasets/polyglot_py/hello-world/solution.py b/evaluator/datasets/polyglot_py/hello-world/solution.py index d695ea115..dea05ae20 100644 --- a/evaluator/datasets/polyglot_py/hello-world/solution.py +++ b/evaluator/datasets/polyglot_py/hello-world/solution.py @@ -1,2 +1,2 @@ def hello(): - return 'Hello, World!' + return "Hello, World!" diff --git a/evaluator/datasets/polyglot_py/hexadecimal/solution.py b/evaluator/datasets/polyglot_py/hexadecimal/solution.py index 672321a89..6499b4d93 100644 --- a/evaluator/datasets/polyglot_py/hexadecimal/solution.py +++ b/evaluator/datasets/polyglot_py/hexadecimal/solution.py @@ -3,8 +3,7 @@ def hexa(hex_string): hex_string = hex_string.lower() - if set(hex_string) - set('0123456789abcdef'): - raise ValueError('Invalid hexadecimal string') - digits = [ord(letter) - ord('a') + 10 if letter in 'abcdef' else ord(letter) - ord('0') - for letter in hex_string] + if set(hex_string) - set("0123456789abcdef"): + raise ValueError("Invalid hexadecimal string") + digits = [ord(letter) - ord("a") + 10 if letter in "abcdef" else ord(letter) - ord("0") for letter in hex_string] return reduce(lambda var_1, var_2: var_1 * 16 + var_2, digits, 0) diff --git a/evaluator/datasets/polyglot_py/hexadecimal/tests.py b/evaluator/datasets/polyglot_py/hexadecimal/tests.py index a7eb7603b..390e97ce3 100644 --- a/evaluator/datasets/polyglot_py/hexadecimal/tests.py +++ b/evaluator/datasets/polyglot_py/hexadecimal/tests.py @@ -8,40 +8,40 @@ class HexadecimalTest(unittest.TestCase): def test_valid_hexa1(self): - self.assertEqual(hexa('1'), 1) + self.assertEqual(hexa("1"), 1) def test_valid_hexa2(self): - self.assertEqual(hexa('c'), 12) + self.assertEqual(hexa("c"), 12) def test_valid_hexa3(self): - self.assertEqual(hexa('10'), 16) + self.assertEqual(hexa("10"), 16) def test_valid_hexa4(self): - self.assertEqual(hexa('af'), 175) + self.assertEqual(hexa("af"), 175) def test_valid_hexa5(self): - self.assertEqual(hexa('100'), 256) + self.assertEqual(hexa("100"), 256) def test_valid_hexa6(self): - self.assertEqual(hexa('19ACE'), 105166) + self.assertEqual(hexa("19ACE"), 105166) def test_valid_hexa7(self): - self.assertEqual(hexa('000000'), 0) + self.assertEqual(hexa("000000"), 0) def test_valid_hexa8(self): - self.assertEqual(hexa('ffff00'), 16776960) + self.assertEqual(hexa("ffff00"), 16776960) def test_valid_hexa9(self): - self.assertEqual(hexa('00fff0'), 65520) + self.assertEqual(hexa("00fff0"), 65520) def test_invalid_hexa(self): with self.assertRaisesWithMessage(ValueError): - hexa('carrot') + hexa("carrot") # Utility functions def assertRaisesWithMessage(self, exception): return self.assertRaisesRegex(exception, r".+") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/house/solution.py b/evaluator/datasets/polyglot_py/house/solution.py index 30e289b91..9d992c513 100644 --- a/evaluator/datasets/polyglot_py/house/solution.py +++ b/evaluator/datasets/polyglot_py/house/solution.py @@ -1,23 +1,24 @@ -PARTS = [('lay in', 'the house that Jack built.'), - ('ate', 'the malt'), - ('killed', 'the rat'), - ('worried', 'the cat'), - ('tossed', 'the dog'), - ('milked', 'the cow with the crumpled horn'), - ('kissed', 'the maiden all forlorn'), - ('married', 'the man all tattered and torn'), - ('woke', 'the priest all shaven and shorn'), - ('kept', 'the rooster that crowed in the morn'), - ('belonged to', 'the farmer sowing his corn'), - ('', 'the horse and the hound and the horn')] +PARTS = [ + ("lay in", "the house that Jack built."), + ("ate", "the malt"), + ("killed", "the rat"), + ("worried", "the cat"), + ("tossed", "the dog"), + ("milked", "the cow with the crumpled horn"), + ("kissed", "the maiden all forlorn"), + ("married", "the man all tattered and torn"), + ("woke", "the priest all shaven and shorn"), + ("kept", "the rooster that crowed in the morn"), + ("belonged to", "the farmer sowing his corn"), + ("", "the horse and the hound and the horn"), +] def verse(verse_num): - verse = [f'This is {PARTS[verse_num][1]}'] - verse.extend(['that {0} {1}'.format(*PARTS[idx]) - for idx in range(verse_num - 1, -1, -1)]) - return ' '.join(verse) + verse = [f"This is {PARTS[verse_num][1]}"] + verse.extend(["that {0} {1}".format(*PARTS[idx]) for idx in range(verse_num - 1, -1, -1)]) + return " ".join(verse) def recite(start_verse, end_verse): - return [verse(verse_num) for verse_num in range(start_verse-1, end_verse)] + return [verse(verse_num) for verse_num in range(start_verse - 1, end_verse)] diff --git a/evaluator/datasets/polyglot_py/house/tests.py b/evaluator/datasets/polyglot_py/house/tests.py index 74372a416..01d4a8589 100644 --- a/evaluator/datasets/polyglot_py/house/tests.py +++ b/evaluator/datasets/polyglot_py/house/tests.py @@ -14,24 +14,18 @@ def test_verse_one_the_house_that_jack_built(self): self.assertEqual(recite(1, 1), ["This is the house that Jack built."]) def test_verse_two_the_malt_that_lay(self): - self.assertEqual( - recite(2, 2), ["This is the malt that lay in the house that Jack built."] - ) + self.assertEqual(recite(2, 2), ["This is the malt that lay in the house that Jack built."]) def test_verse_three_the_rat_that_ate(self): self.assertEqual( recite(3, 3), - [ - "This is the rat that ate the malt that lay in the house that Jack built." - ], + ["This is the rat that ate the malt that lay in the house that Jack built."], ) def test_verse_four_the_cat_that_killed(self): self.assertEqual( recite(4, 4), - [ - "This is the cat that killed the rat that ate the malt that lay in the house that Jack built." - ], + ["This is the cat that killed the rat that ate the malt that lay in the house that Jack built."], ) def test_verse_five_the_dog_that_worried(self): diff --git a/evaluator/datasets/polyglot_py/isbn-verifier/solution.py b/evaluator/datasets/polyglot_py/isbn-verifier/solution.py index f2f14f394..8e5441155 100644 --- a/evaluator/datasets/polyglot_py/isbn-verifier/solution.py +++ b/evaluator/datasets/polyglot_py/isbn-verifier/solution.py @@ -1,7 +1,7 @@ def is_valid(isbn): - chars = list(isbn.replace('-', '')) - if chars and chars[-1] == 'X': - chars[-1] = '10' + chars = list(isbn.replace("-", "")) + if chars and chars[-1] == "X": + chars[-1] = "10" if not len(chars) == 10 or not all(char.isdigit() for char in chars): return False indices = list(range(10, 0, -1)) diff --git a/evaluator/datasets/polyglot_py/killer-sudoku-helper/solution.py b/evaluator/datasets/polyglot_py/killer-sudoku-helper/solution.py index ac3a7f879..a789bf9e2 100644 --- a/evaluator/datasets/polyglot_py/killer-sudoku-helper/solution.py +++ b/evaluator/datasets/polyglot_py/killer-sudoku-helper/solution.py @@ -1,10 +1,9 @@ import itertools + def combinations(target, size, exclude): result = [] - possible = [index for index in - range(1, int((target ** 2 /size) ** 0.6)) - if index not in exclude] + possible = [index for index in range(1, int((target**2 / size) ** 0.6)) if index not in exclude] if size == 1: return [[target]] diff --git a/evaluator/datasets/polyglot_py/kindergarten-garden/solution.py b/evaluator/datasets/polyglot_py/kindergarten-garden/solution.py index 262c6ad6e..33f66e914 100644 --- a/evaluator/datasets/polyglot_py/kindergarten-garden/solution.py +++ b/evaluator/datasets/polyglot_py/kindergarten-garden/solution.py @@ -1,20 +1,19 @@ class Garden: - STUDENTS = [ - 'Alice', - 'Bob', - 'Charlie', - 'David', - 'Eve', - 'Fred', - 'Ginny', - 'Harriet', - 'Ileana', - 'Joseph', - 'Kincaid', - 'Larry', + "Alice", + "Bob", + "Charlie", + "David", + "Eve", + "Fred", + "Ginny", + "Harriet", + "Ileana", + "Joseph", + "Kincaid", + "Larry", ] - PLANTS = {'C': 'Clover', 'G': 'Grass', 'R': 'Radishes', 'V': 'Violets'} + PLANTS = {"C": "Clover", "G": "Grass", "R": "Radishes", "V": "Violets"} def __init__(self, diagram, students=None): students = sorted(students or self.STUDENTS) @@ -24,12 +23,8 @@ def __init__(self, diagram, students=None): start = idx * 2 stop = start + 2 self.cups.setdefault(student, []) - self.cups[student].extend( - self.PLANTS[plant] for plant in front[start:stop] - ) - self.cups[student].extend( - self.PLANTS[plant] for plant in back[start:stop] - ) + self.cups[student].extend(self.PLANTS[plant] for plant in front[start:stop]) + self.cups[student].extend(self.PLANTS[plant] for plant in back[start:stop]) def plants(self, student): return self.cups.get(student, []) diff --git a/evaluator/datasets/polyglot_py/kindergarten-garden/tests.py b/evaluator/datasets/polyglot_py/kindergarten-garden/tests.py index a7fc25e16..24a0aaa21 100644 --- a/evaluator/datasets/polyglot_py/kindergarten-garden/tests.py +++ b/evaluator/datasets/polyglot_py/kindergarten-garden/tests.py @@ -12,21 +12,15 @@ class KindergartenGardenTest(unittest.TestCase): def test_partial_garden_garden_with_single_student(self): garden = Garden("RC\nGG") - self.assertEqual( - garden.plants("Alice"), ["Radishes", "Clover", "Grass", "Grass"] - ) + self.assertEqual(garden.plants("Alice"), ["Radishes", "Clover", "Grass", "Grass"]) def test_partial_garden_different_garden_with_single_student(self): garden = Garden("VC\nRC") - self.assertEqual( - garden.plants("Alice"), ["Violets", "Clover", "Radishes", "Clover"] - ) + self.assertEqual(garden.plants("Alice"), ["Violets", "Clover", "Radishes", "Clover"]) def test_partial_garden_garden_with_two_students(self): garden = Garden("VVCG\nVVRC") - self.assertEqual( - garden.plants("Bob"), ["Clover", "Grass", "Radishes", "Clover"] - ) + self.assertEqual(garden.plants("Bob"), ["Clover", "Grass", "Radishes", "Clover"]) def test_partial_garden_second_student_s_garden(self): garden = Garden("VVCCGG\nVVCCGG") @@ -38,9 +32,7 @@ def test_partial_garden_third_student_s_garden(self): def test_full_garden_for_alice_first_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Alice"), ["Violets", "Radishes", "Violets", "Radishes"] - ) + self.assertEqual(garden.plants("Alice"), ["Violets", "Radishes", "Violets", "Radishes"]) def test_full_garden_for_bob_second_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") @@ -48,15 +40,11 @@ def test_full_garden_for_bob_second_student_s_garden(self): def test_full_garden_for_charlie(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Charlie"), ["Violets", "Violets", "Clover", "Grass"] - ) + self.assertEqual(garden.plants("Charlie"), ["Violets", "Violets", "Clover", "Grass"]) def test_full_garden_for_david(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("David"), ["Radishes", "Violets", "Clover", "Radishes"] - ) + self.assertEqual(garden.plants("David"), ["Radishes", "Violets", "Clover", "Radishes"]) def test_full_garden_for_eve(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") @@ -64,9 +52,7 @@ def test_full_garden_for_eve(self): def test_full_garden_for_fred(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Fred"), ["Grass", "Clover", "Violets", "Clover"] - ) + self.assertEqual(garden.plants("Fred"), ["Grass", "Clover", "Violets", "Clover"]) def test_full_garden_for_ginny(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") @@ -74,48 +60,30 @@ def test_full_garden_for_ginny(self): def test_full_garden_for_harriet(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Harriet"), ["Violets", "Radishes", "Radishes", "Violets"] - ) + self.assertEqual(garden.plants("Harriet"), ["Violets", "Radishes", "Radishes", "Violets"]) def test_full_garden_for_ileana(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Ileana"), ["Grass", "Clover", "Violets", "Clover"] - ) + self.assertEqual(garden.plants("Ileana"), ["Grass", "Clover", "Violets", "Clover"]) def test_full_garden_for_joseph(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Joseph"), ["Violets", "Clover", "Violets", "Grass"] - ) + self.assertEqual(garden.plants("Joseph"), ["Violets", "Clover", "Violets", "Grass"]) def test_full_garden_for_kincaid_second_to_last_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Kincaid"), ["Grass", "Clover", "Clover", "Grass"] - ) + self.assertEqual(garden.plants("Kincaid"), ["Grass", "Clover", "Clover", "Grass"]) def test_full_garden_for_larry_last_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Larry"), ["Grass", "Violets", "Clover", "Violets"] - ) + self.assertEqual(garden.plants("Larry"), ["Grass", "Violets", "Clover", "Violets"]) # Additional tests for this track def test_students_are_unordered_first_student(self): - garden = Garden( - "VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"] - ) - self.assertEqual( - garden.plants("Patricia"), ["Violets", "Clover", "Radishes", "Violets"] - ) + garden = Garden("VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"]) + self.assertEqual(garden.plants("Patricia"), ["Violets", "Clover", "Radishes", "Violets"]) def test_students_are_unordered_last_student(self): - garden = Garden( - "VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"] - ) - self.assertEqual( - garden.plants("Xander"), ["Radishes", "Grass", "Clover", "Violets"] - ) + garden = Garden("VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"]) + self.assertEqual(garden.plants("Xander"), ["Radishes", "Grass", "Clover", "Violets"]) diff --git a/evaluator/datasets/polyglot_py/knapsack/solution.py b/evaluator/datasets/polyglot_py/knapsack/solution.py index 548dd5a2b..017d160f7 100644 --- a/evaluator/datasets/polyglot_py/knapsack/solution.py +++ b/evaluator/datasets/polyglot_py/knapsack/solution.py @@ -1,12 +1,10 @@ def maximum_value(maximum_weight, items): - totals = [[0 for _ in range(len(items) + 1)] - for _ in range(maximum_weight + 1)] + totals = [[0 for _ in range(len(items) + 1)] for _ in range(maximum_weight + 1)] for weight in range(1, maximum_weight + 1): for index, item in enumerate(items, 1): - if item['weight'] <= weight: - value = item['value'] + \ - totals[weight - item['weight']][index - 1] + if item["weight"] <= weight: + value = item["value"] + totals[weight - item["weight"]][index - 1] value_without_item = totals[weight][index - 1] totals[weight][index] = max(value, value_without_item) diff --git a/evaluator/datasets/polyglot_py/largest-series-product/solution.py b/evaluator/datasets/polyglot_py/largest-series-product/solution.py index f7cfc2310..9f583c677 100644 --- a/evaluator/datasets/polyglot_py/largest-series-product/solution.py +++ b/evaluator/datasets/polyglot_py/largest-series-product/solution.py @@ -5,19 +5,18 @@ def slices(series, size): if not size <= len(series): - raise ValueError('span must not exceed string length') + raise ValueError("span must not exceed string length") elif not 0 < size: - raise ValueError('span must not be negative') + raise ValueError("span must not be negative") elif not all(item.isdigit() for item in series): - raise ValueError('digits input must only contain digits') + raise ValueError("digits input must only contain digits") numbers = [int(digit) for digit in series] - return [numbers[idx:idx + size] - for idx in range(len(numbers) - size + 1)] + return [numbers[idx : idx + size] for idx in range(len(numbers) - size + 1)] def largest_product(series, size): if size == 0: return 1 - return max(reduce(mul, slice) for slice in slices(series, size)) \ No newline at end of file + return max(reduce(mul, slice) for slice in slices(series, size)) diff --git a/evaluator/datasets/polyglot_py/ledger/main.py b/evaluator/datasets/polyglot_py/ledger/main.py index 4f16be896..362e4ba24 100644 --- a/evaluator/datasets/polyglot_py/ledger/main.py +++ b/evaluator/datasets/polyglot_py/ledger/main.py @@ -11,27 +11,27 @@ def __init__(self) -> None: def create_entry(date: str, description: str, change: int) -> LedgerEntry: entry = LedgerEntry() - entry.date = datetime.strptime(date, '%Y-%m-%d') + entry.date = datetime.strptime(date, "%Y-%m-%d") entry.description = description entry.change = change return entry def format_entries(currency: str, locale: str, entries: list[LedgerEntry]) -> str: - if locale == 'en_US': + if locale == "en_US": # Generate Header Row - table = 'Date' + table = "Date" for _ in range(7): - table += ' ' - table += '| Description' + table += " " + table += "| Description" for _ in range(15): - table += ' ' - table += '| Change' + table += " " + table += "| Change" for _ in range(7): - table += ' ' + table += " " while len(entries) > 0: - table += '\n' + table += "\n" # Find next entry in order min_entry_index = -1 @@ -44,16 +44,13 @@ def format_entries(currency: str, locale: str, entries: list[LedgerEntry]) -> st if entry.date < min_entry.date: min_entry_index = i continue - if ( - entry.date == min_entry.date and - entry.change < min_entry.change - ): + if entry.date == min_entry.date and entry.change < min_entry.change: min_entry_index = i continue if ( - entry.date == min_entry.date and - entry.change == min_entry.change and - entry.description < min_entry.description + entry.date == min_entry.date + and entry.change == min_entry.change + and entry.description < min_entry.description ): min_entry_index = i continue @@ -64,117 +61,117 @@ def format_entries(currency: str, locale: str, entries: list[LedgerEntry]) -> st month = entry.date.month month = str(month) if len(month) < 2: - month = '0' + month + month = "0" + month date_str = month - date_str += '/' + date_str += "/" day = entry.date.day day = str(day) if len(day) < 2: - day = '0' + day + day = "0" + day date_str += day - date_str += '/' + date_str += "/" year = entry.date.year year = str(year) while len(year) < 4: - year = '0' + year + year = "0" + year date_str += year table += date_str - table += ' | ' + table += " | " # Write entry description to table # Truncate if necessary if len(entry.description) > 25: for i in range(22): table += entry.description[i] - table += '...' + table += "..." else: for i in range(25): if len(entry.description) > i: table += entry.description[i] else: - table += ' ' - table += ' | ' + table += " " + table += " | " # Write entry change to table - if currency == 'USD': - change_str = '' + if currency == "USD": + change_str = "" if entry.change < 0: - change_str = '(' - change_str += '$' + change_str = "(" + change_str += "$" change_dollar = abs(int(entry.change / 100.0)) dollar_parts = [] while change_dollar > 0: dollar_parts.insert(0, str(change_dollar % 1000)) change_dollar = change_dollar // 1000 if len(dollar_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += dollar_parts[0] dollar_parts.pop(0) if len(dollar_parts) == 0: break - change_str += ',' - change_str += '.' + change_str += "," + change_str += "." change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents if entry.change < 0: - change_str += ')' + change_str += ")" else: - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str - elif currency == 'EUR': - change_str = '' + elif currency == "EUR": + change_str = "" if entry.change < 0: - change_str = '(' - change_str += u'€' + change_str = "(" + change_str += "€" change_euro = abs(int(entry.change / 100.0)) euro_parts = [] while change_euro > 0: euro_parts.insert(0, str(change_euro % 1000)) change_euro = change_euro // 1000 if len(euro_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += euro_parts[0] euro_parts.pop(0) if len(euro_parts) == 0: break - change_str += ',' - change_str += '.' + change_str += "," + change_str += "." change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents if entry.change < 0: - change_str += ')' + change_str += ")" else: - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str return table - elif locale == 'nl_NL': + elif locale == "nl_NL": # Generate Header Row - table = 'Datum' + table = "Datum" for _ in range(6): - table += ' ' - table += '| Omschrijving' + table += " " + table += "| Omschrijving" for _ in range(14): - table += ' ' - table += '| Verandering' + table += " " + table += "| Verandering" for _ in range(2): - table += ' ' + table += " " while len(entries) > 0: - table += '\n' + table += "\n" # Find next entry in order min_entry_index = -1 @@ -187,16 +184,13 @@ def format_entries(currency: str, locale: str, entries: list[LedgerEntry]) -> st if entry.date < min_entry.date: min_entry_index = i continue - if ( - entry.date == min_entry.date and - entry.change < min_entry.change - ): + if entry.date == min_entry.date and entry.change < min_entry.change: min_entry_index = i continue if ( - entry.date == min_entry.date and - entry.change == min_entry.change and - entry.description < min_entry.description + entry.date == min_entry.date + and entry.change == min_entry.change + and entry.description < min_entry.description ): min_entry_index = i continue @@ -207,93 +201,92 @@ def format_entries(currency: str, locale: str, entries: list[LedgerEntry]) -> st day = entry.date.day day = str(day) if len(day) < 2: - day = '0' + day + day = "0" + day date_str = day - date_str += '-' + date_str += "-" month = entry.date.month month = str(month) if len(month) < 2: - month = '0' + month + month = "0" + month date_str += month - date_str += '-' + date_str += "-" year = entry.date.year year = str(year) while len(year) < 4: - year = '0' + year + year = "0" + year date_str += year table += date_str - table += ' | ' + table += " | " # Write entry description to table # Truncate if necessary if len(entry.description) > 25: for i in range(22): table += entry.description[i] - table += '...' + table += "..." else: for i in range(25): if len(entry.description) > i: table += entry.description[i] else: - table += ' ' - table += ' | ' + table += " " + table += " | " # Write entry change to table - if currency == 'USD': - change_str = '$ ' + if currency == "USD": + change_str = "$ " if entry.change < 0: - change_str += '-' + change_str += "-" change_dollar = abs(int(entry.change / 100.0)) dollar_parts = [] while change_dollar > 0: dollar_parts.insert(0, str(change_dollar % 1000)) change_dollar = change_dollar // 1000 if len(dollar_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += dollar_parts[0] dollar_parts.pop(0) if len(dollar_parts) == 0: break - change_str += '.' - change_str += ',' + change_str += "." + change_str += "," change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str - elif currency == 'EUR': - change_str = u'€ ' + elif currency == "EUR": + change_str = "€ " if entry.change < 0: - change_str += '-' + change_str += "-" change_euro = abs(int(entry.change / 100.0)) euro_parts = [] while change_euro > 0: euro_parts.insert(0, str(change_euro % 1000)) change_euro = change_euro // 1000 if len(euro_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += euro_parts[0] euro_parts.pop(0) if len(euro_parts) == 0: break - change_str += '.' - change_str += ',' + change_str += "." + change_str += "," change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str return table - diff --git a/evaluator/datasets/polyglot_py/ledger/solution.py b/evaluator/datasets/polyglot_py/ledger/solution.py index 46e8e3c9f..85aac97d1 100644 --- a/evaluator/datasets/polyglot_py/ledger/solution.py +++ b/evaluator/datasets/polyglot_py/ledger/solution.py @@ -1,39 +1,39 @@ # -*- coding: utf-8 -*- from datetime import datetime -ROW_FMT = '{{:<{1}}} | {{:<{2}}} | {{:{0}{3}}}' +ROW_FMT = "{{:<{1}}} | {{:<{2}}} | {{:{0}{3}}}" def truncate(s, length=25): if len(s) <= length: return s - return s[:length - 3] + '...' + return s[: length - 3] + "..." class LCInfo: def __init__(self, locale, currency, columns): self.columns = columns - if locale == 'en_US': - headers = ['Date', 'Description', 'Change'] - self.datefmt = '{0.month:02}/{0.day:02}/{0.year:04}' - self.cur_fmt = '{}{}{}{}' - self.lead_neg = '(' - self.trail_neg = ')' - self.thousands = ',' - self.decimal = '.' - elif locale == 'nl_NL': - headers = ['Datum', 'Omschrijving', 'Verandering'] - self.datefmt = '{0.day:02}-{0.month:02}-{0.year:04}' - self.cur_fmt = '{1} {0}{2}{3}' - self.lead_neg = '-' - self.trail_neg = ' ' - self.thousands = '.' - self.decimal = ',' - fmt = ROW_FMT.format('<', *columns) + if locale == "en_US": + headers = ["Date", "Description", "Change"] + self.datefmt = "{0.month:02}/{0.day:02}/{0.year:04}" + self.cur_fmt = "{}{}{}{}" + self.lead_neg = "(" + self.trail_neg = ")" + self.thousands = "," + self.decimal = "." + elif locale == "nl_NL": + headers = ["Datum", "Omschrijving", "Verandering"] + self.datefmt = "{0.day:02}-{0.month:02}-{0.year:04}" + self.cur_fmt = "{1} {0}{2}{3}" + self.lead_neg = "-" + self.trail_neg = " " + self.thousands = "." + self.decimal = "," + fmt = ROW_FMT.format("<", *columns) self.headers = fmt.format(*headers) self.cur_symbol = { - 'USD': '$', - 'EUR': '€', + "USD": "$", + "EUR": "€", }.get(currency) def number(self, n): @@ -42,23 +42,23 @@ def number(self, n): while n_int > 0: n_int, idx = divmod(n_int, 1000) n_int_parts.insert(0, str(idx)) - return '{}{}{:02}'.format( - self.thousands.join(n_int_parts) or '0', + return "{}{}{:02}".format( + self.thousands.join(n_int_parts) or "0", self.decimal, n_float, ) def currency(self, change): return self.cur_fmt.format( - self.lead_neg if change < 0 else '', + self.lead_neg if change < 0 else "", self.cur_symbol, self.number(change), - self.trail_neg if change < 0 else ' ', + self.trail_neg if change < 0 else " ", ) def entry(self, entry): date, change, desc = entry - fmt = ROW_FMT.format('>', *self.columns) + fmt = ROW_FMT.format(">", *self.columns) return fmt.format( self.datefmt.format(date), truncate(desc), @@ -68,15 +68,11 @@ def entry(self, entry): def table(self, entries): lines = [self.headers] lines.extend(map(self.entry, sorted(entries))) - return '\n'.join(lines) + return "\n".join(lines) def create_entry(date, description, change): - return ( - datetime.strptime(date, '%Y-%m-%d'), - change, - description - ) + return (datetime.strptime(date, "%Y-%m-%d"), change, description) def format_entries(currency, locale, entries): diff --git a/evaluator/datasets/polyglot_py/ledger/tests.py b/evaluator/datasets/polyglot_py/ledger/tests.py index e82758a64..d264bc4c5 100644 --- a/evaluator/datasets/polyglot_py/ledger/tests.py +++ b/evaluator/datasets/polyglot_py/ledger/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - format_entries, create_entry, + format_entries, ) diff --git a/evaluator/datasets/polyglot_py/linked-list/main.py b/evaluator/datasets/polyglot_py/linked-list/main.py index 7b2a9386c..806e9d9bc 100644 --- a/evaluator/datasets/polyglot_py/linked-list/main.py +++ b/evaluator/datasets/polyglot_py/linked-list/main.py @@ -2,7 +2,7 @@ class Node: - def __init__(self, value: Any, succeeding: 'Node | None' = None, previous: 'Node | None' = None) -> None: + def __init__(self, value: Any, succeeding: "Node | None" = None, previous: "Node | None" = None) -> None: pass diff --git a/evaluator/datasets/polyglot_py/list-ops/main.py b/evaluator/datasets/polyglot_py/list-ops/main.py index 6021e420e..269666e64 100644 --- a/evaluator/datasets/polyglot_py/list-ops/main.py +++ b/evaluator/datasets/polyglot_py/list-ops/main.py @@ -1,4 +1,4 @@ -from typing import Callable, Any +from typing import Any, Callable def append(list1: list, list2: list) -> list: diff --git a/evaluator/datasets/polyglot_py/list-ops/tests.py b/evaluator/datasets/polyglot_py/list-ops/tests.py index 8677b524e..4badfd136 100644 --- a/evaluator/datasets/polyglot_py/list-ops/tests.py +++ b/evaluator/datasets/polyglot_py/list-ops/tests.py @@ -11,7 +11,11 @@ foldr, length, reverse, +) +from main import ( filter as list_ops_filter, +) +from main import ( map as list_ops_map, ) @@ -84,17 +88,13 @@ def test_reverse_non_empty_list(self): self.assertEqual(reverse([1, 3, 5, 7]), [7, 5, 3, 1]) def test_reverse_list_of_lists_is_not_flattened(self): - self.assertEqual( - reverse([[1, 2], [3], [], [4, 5, 6]]), [[4, 5, 6], [], [3], [1, 2]] - ) + self.assertEqual(reverse([[1, 2], [3], [], [4, 5, 6]]), [[4, 5, 6], [], [3], [1, 2]]) # Additional tests for this track def test_foldr_foldr_add_string(self): self.assertEqual( - foldr( - lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!" - ), + foldr(lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"), "exercism!", ) diff --git a/evaluator/datasets/polyglot_py/luhn/solution.py b/evaluator/datasets/polyglot_py/luhn/solution.py index 32a4dbc48..1380d8c93 100644 --- a/evaluator/datasets/polyglot_py/luhn/solution.py +++ b/evaluator/datasets/polyglot_py/luhn/solution.py @@ -2,7 +2,7 @@ class Luhn: def __init__(self, card_num): self.card_num = card_num self.checksum = -1 - digits = card_num.replace(' ', '') + digits = card_num.replace(" ", "") length = len(digits) if digits.isdigit() and length > 1: self.checksum = 0 diff --git a/evaluator/datasets/polyglot_py/markdown/main.py b/evaluator/datasets/polyglot_py/markdown/main.py index 6f62444a2..3f4ed8165 100644 --- a/evaluator/datasets/polyglot_py/markdown/main.py +++ b/evaluator/datasets/polyglot_py/markdown/main.py @@ -2,76 +2,72 @@ def parse(markdown: str) -> str: - lines = markdown.split('\n') - res = '' + lines = markdown.split("\n") + res = "" in_list = False in_list_append = False for i in lines: - if re.match('###### (.*)', i) is not None: - i = '
' + i[7:] + '
' - elif re.match('##### (.*)', i) is not None: - i = '
' + i[6:] + '
' - elif re.match('#### (.*)', i) is not None: - i = '

' + i[5:] + '

' - elif re.match('### (.*)', i) is not None: - i = '

' + i[4:] + '

' - elif re.match('## (.*)', i) is not None: - i = '

' + i[3:] + '

' - elif re.match('# (.*)', i) is not None: - i = '

' + i[2:] + '

' - m = re.match(r'\* (.*)', i) + if re.match("###### (.*)", i) is not None: + i = "
" + i[7:] + "
" + elif re.match("##### (.*)", i) is not None: + i = "
" + i[6:] + "
" + elif re.match("#### (.*)", i) is not None: + i = "

" + i[5:] + "

" + elif re.match("### (.*)", i) is not None: + i = "

" + i[4:] + "

" + elif re.match("## (.*)", i) is not None: + i = "

" + i[3:] + "

" + elif re.match("# (.*)", i) is not None: + i = "

" + i[2:] + "

" + m = re.match(r"\* (.*)", i) if m: if not in_list: in_list = True is_bold = False is_italic = False curr = m.group(1) - m1 = re.match('(.*)__(.*)__(.*)', curr) + m1 = re.match("(.*)__(.*)__(.*)", curr) if m1: - curr = m1.group(1) + '' + \ - m1.group(2) + '' + m1.group(3) + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) is_bold = True - m1 = re.match('(.*)_(.*)_(.*)', curr) + m1 = re.match("(.*)_(.*)_(.*)", curr) if m1: - curr = m1.group(1) + '' + m1.group(2) + \ - '' + m1.group(3) + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) is_italic = True - i = '
  • ' + curr + '
  • ' + i = "
    • " + curr + "
    • " else: is_bold = False is_italic = False curr = m.group(1) - m1 = re.match('(.*)__(.*)__(.*)', curr) + m1 = re.match("(.*)__(.*)__(.*)", curr) if m1: is_bold = True - m1 = re.match('(.*)_(.*)_(.*)', curr) + m1 = re.match("(.*)_(.*)_(.*)", curr) if m1: is_italic = True if is_bold: - curr = m1.group(1) + '' + \ - m1.group(2) + '' + m1.group(3) + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) if is_italic: - curr = m1.group(1) + '' + m1.group(2) + \ - '' + m1.group(3) - i = '
    • ' + curr + '
    • ' + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) + i = "
    • " + curr + "
    • " else: if in_list: in_list_append = True in_list = False - m = re.match('' - m = re.match('(.*)__(.*)__(.*)', i) + i = "

      " + i + "

      " + m = re.match("(.*)__(.*)__(.*)", i) if m: - i = m.group(1) + '' + m.group(2) + '' + m.group(3) - m = re.match('(.*)_(.*)_(.*)', i) + i = m.group(1) + "" + m.group(2) + "" + m.group(3) + m = re.match("(.*)_(.*)_(.*)", i) if m: - i = m.group(1) + '' + m.group(2) + '' + m.group(3) + i = m.group(1) + "" + m.group(2) + "" + m.group(3) if in_list_append: - i = '
    ' + i + i = "
" + i in_list_append = False res += i if in_list: - res += '' + res += "" return res diff --git a/evaluator/datasets/polyglot_py/markdown/solution.py b/evaluator/datasets/polyglot_py/markdown/solution.py index 1c9d04cf9..282c8cae7 100644 --- a/evaluator/datasets/polyglot_py/markdown/solution.py +++ b/evaluator/datasets/polyglot_py/markdown/solution.py @@ -2,49 +2,47 @@ def parse(markdown): - lines = markdown.split('\n') - html = '' + lines = markdown.split("\n") + html = "" in_list = False in_list_append = False for line in lines: result = parse_line(line, in_list, in_list_append) - html += result['line'] - in_list = result['in_list'] - in_list_append = result['in_list_append'] + html += result["line"] + in_list = result["in_list"] + in_list_append = result["in_list_append"] if in_list: - html += '' + html += "" return html def wrap(line, tag): - return '<{tag}>{line}'.format(line=line, tag=tag) + return "<{tag}>{line}".format(line=line, tag=tag) def check_headers(line): - pattern = '# (.*)' + pattern = "# (.*)" for index in range(6): if re.match(pattern, line): - return wrap(line[(index + 2):], 'h' + str(index + 1)) - pattern = '#' + pattern + return wrap(line[(index + 2) :], "h" + str(index + 1)) + pattern = "#" + pattern return line def check_bold(line): - bold_pattern = '(.*)__(.*)__(.*)' + bold_pattern = "(.*)__(.*)__(.*)" bold_match = re.match(bold_pattern, line) if bold_match: - return bold_match.group(1) + wrap(bold_match.group(2), 'strong')\ - + bold_match.group(3) + return bold_match.group(1) + wrap(bold_match.group(2), "strong") + bold_match.group(3) else: return None def check_italic(line): - italic_pattern = '(.*)_(.*)_(.*)' + italic_pattern = "(.*)_(.*)_(.*)" italic_match = re.match(italic_pattern, line) if italic_match: - return italic_match.group(1) + wrap(italic_match.group(2), 'em')\ - + italic_match.group(3) + return italic_match.group(1) + wrap(italic_match.group(2), "em") + italic_match.group(3) else: return None @@ -52,25 +50,24 @@ def check_italic(line): def parse_line(line, in_list, in_list_append): result = check_headers(line) - list_match = re.match(r'\* (.*)', result) + list_match = re.match(r"\* (.*)", result) if list_match: if not in_list: - result = '
    ' + wrap(list_match.group(1), 'li') + result = "
      " + wrap(list_match.group(1), "li") in_list = True else: - result = wrap(list_match.group(1), 'li') + result = wrap(list_match.group(1), "li") else: if in_list: in_list_append = True in_list = False - if not re.match(')(.*)()(.*)', - r'\1\2

      \3

      \4\5', result) + result = re.sub("(.*)(
    • )(.*)(
    • )(.*)", r"\1\2

      \3

      \4\5", result) while check_bold(result): result = check_bold(result) @@ -78,11 +75,7 @@ def parse_line(line, in_list, in_list_append): result = check_italic(result) if in_list_append: - result = '
    ' + result + result = "
" + result in_list_append = False - return { - 'line': result, - 'in_list': in_list, - 'in_list_append': in_list_append - } + return {"line": result, "in_list": in_list, "in_list_append": in_list_append} diff --git a/evaluator/datasets/polyglot_py/markdown/tests.py b/evaluator/datasets/polyglot_py/markdown/tests.py index c48d2912b..f39c04d55 100644 --- a/evaluator/datasets/polyglot_py/markdown/tests.py +++ b/evaluator/datasets/polyglot_py/markdown/tests.py @@ -11,19 +11,13 @@ class MarkdownTest(unittest.TestCase): def test_parses_normal_text_as_a_paragraph(self): - self.assertEqual( - parse("This will be a paragraph"), "

This will be a paragraph

" - ) + self.assertEqual(parse("This will be a paragraph"), "

This will be a paragraph

") def test_parsing_italics(self): - self.assertEqual( - parse("_This will be italic_"), "

This will be italic

" - ) + self.assertEqual(parse("_This will be italic_"), "

This will be italic

") def test_parsing_bold_text(self): - self.assertEqual( - parse("__This will be bold__"), "

This will be bold

" - ) + self.assertEqual(parse("__This will be bold__"), "

This will be bold

") def test_mixed_normal_italics_and_bold_text(self): self.assertEqual( @@ -41,19 +35,13 @@ def test_with_h3_header_level(self): self.assertEqual(parse("### This will be an h3"), "

This will be an h3

") def test_with_h4_header_level(self): - self.assertEqual( - parse("#### This will be an h4"), "

This will be an h4

" - ) + self.assertEqual(parse("#### This will be an h4"), "

This will be an h4

") def test_with_h5_header_level(self): - self.assertEqual( - parse("##### This will be an h5"), "
This will be an h5
" - ) + self.assertEqual(parse("##### This will be an h5"), "
This will be an h5
") def test_with_h6_header_level(self): - self.assertEqual( - parse("###### This will be an h6"), "
This will be an h6
" - ) + self.assertEqual(parse("###### This will be an h6"), "
This will be an h6
") def test_h7_header_level_is_a_paragraph(self): self.assertEqual( @@ -62,9 +50,7 @@ def test_h7_header_level_is_a_paragraph(self): ) def test_unordered_lists(self): - self.assertEqual( - parse("* Item 1\n* Item 2"), "
  • Item 1
  • Item 2
" - ) + self.assertEqual(parse("* Item 1\n* Item 2"), "
  • Item 1
  • Item 2
") def test_with_a_little_bit_of_everything(self): self.assertEqual( diff --git a/evaluator/datasets/polyglot_py/matching-brackets/solution.py b/evaluator/datasets/polyglot_py/matching-brackets/solution.py index b37f13207..e219e9d8a 100644 --- a/evaluator/datasets/polyglot_py/matching-brackets/solution.py +++ b/evaluator/datasets/polyglot_py/matching-brackets/solution.py @@ -1,5 +1,5 @@ def is_paired(input_string): - counterparts = {')': '(', '}': '{', ']': '['} + counterparts = {")": "(", "}": "{", "]": "["} stack = [] for char in input_string: diff --git a/evaluator/datasets/polyglot_py/matching-brackets/tests.py b/evaluator/datasets/polyglot_py/matching-brackets/tests.py index f43715bc3..444778bf2 100644 --- a/evaluator/datasets/polyglot_py/matching-brackets/tests.py +++ b/evaluator/datasets/polyglot_py/matching-brackets/tests.py @@ -69,8 +69,6 @@ def test_math_expression(self): def test_complex_latex_expression(self): self.assertEqual( - is_paired( - "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)" - ), + is_paired("\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)"), True, ) diff --git a/evaluator/datasets/polyglot_py/matrix/solution.py b/evaluator/datasets/polyglot_py/matrix/solution.py index 3c89844be..de82ed20f 100644 --- a/evaluator/datasets/polyglot_py/matrix/solution.py +++ b/evaluator/datasets/polyglot_py/matrix/solution.py @@ -1,7 +1,6 @@ class Matrix: def __init__(self, matrix_string): - self.rows = [[int(number) for number in row.split()] - for row in matrix_string.split('\n')] + self.rows = [[int(number) for number in row.split()] for row in matrix_string.split("\n")] self.columns = [list(tup) for tup in zip(*self.rows)] def row(self, index): diff --git a/evaluator/datasets/polyglot_py/meetup/main.py b/evaluator/datasets/polyglot_py/meetup/main.py index 1e8f6f5f8..44248a368 100644 --- a/evaluator/datasets/polyglot_py/meetup/main.py +++ b/evaluator/datasets/polyglot_py/meetup/main.py @@ -8,6 +8,7 @@ class MeetupDayException(ValueError): message: explanation of the error. """ + def __init__(self) -> None: pass diff --git a/evaluator/datasets/polyglot_py/meetup/solution.py b/evaluator/datasets/polyglot_py/meetup/solution.py index d54710748..43766ca7d 100644 --- a/evaluator/datasets/polyglot_py/meetup/solution.py +++ b/evaluator/datasets/polyglot_py/meetup/solution.py @@ -2,25 +2,27 @@ def meetup(year, month, week, day_of_week): - candidates = [date - for date in Calendar().itermonthdates(year, month) - if date.month == month - if date.strftime('%A') == day_of_week] + candidates = [ + date + for date in Calendar().itermonthdates(year, month) + if date.month == month + if date.strftime("%A") == day_of_week + ] return _choice(week)(candidates) def _choice(week): - if week == 'teenth': - return lambda dates: next(date for date in dates if - 13 <= date.day <= 19) + if week == "teenth": + return lambda dates: next(date for date in dates if 13 <= date.day <= 19) - ordinals = ('first', 'second', 'third', 'fourth', 'fifth', 'sixth') - day = -1 if (week == 'last') else (ordinals.index(week)) + ordinals = ("first", "second", "third", "fourth", "fifth", "sixth") + day = -1 if (week == "last") else (ordinals.index(week)) def _func(dates): if day < len(dates): return dates[day] - raise MeetupDayException('That day does not exist.') + raise MeetupDayException("That day does not exist.") + return _func @@ -30,5 +32,6 @@ class MeetupDayException(ValueError): message: explanation of the error. """ + def __init__(self, message): self.message = message diff --git a/evaluator/datasets/polyglot_py/meetup/tests.py b/evaluator/datasets/polyglot_py/meetup/tests.py index 5e8960b8b..14b420191 100644 --- a/evaluator/datasets/polyglot_py/meetup/tests.py +++ b/evaluator/datasets/polyglot_py/meetup/tests.py @@ -2,12 +2,12 @@ # https://github.com/exercism/problem-specifications/tree/main/exercises/meetup/canonical-data.json # File last updated on 2023-07-19 -from datetime import date import unittest +from datetime import date from main import ( - meetup, MeetupDayException, + meetup, ) diff --git a/evaluator/datasets/polyglot_py/minesweeper/solution.py b/evaluator/datasets/polyglot_py/minesweeper/solution.py index 4ebbc8413..33c3228ea 100644 --- a/evaluator/datasets/polyglot_py/minesweeper/solution.py +++ b/evaluator/datasets/polyglot_py/minesweeper/solution.py @@ -8,33 +8,33 @@ def annotate(minefield): for index1 in range(col_len): for index2 in range(row_len): - if board[index1][index2] != ' ': + if board[index1][index2] != " ": continue low = max(index2 - 1, 0) high = min(index2 + 2, row_len + 2) - counts = minefield[index1][low:high].count('*') + counts = minefield[index1][low:high].count("*") if index1 > 0: - counts += minefield[index1 - 1][low:high].count('*') + counts += minefield[index1 - 1][low:high].count("*") if index1 < col_len - 1: - counts += minefield[index1 + 1][low:high].count('*') + counts += minefield[index1 + 1][low:high].count("*") if counts == 0: continue board[index1][index2] = str(counts) - return [''.join(row) for row in board] + return ["".join(row) for row in board] def verify_board(minefield): # Rows with different lengths row_len = len(minefield[0]) if not all(len(row) == row_len for row in minefield): - raise ValueError('The board is invalid with current input.') + raise ValueError("The board is invalid with current input.") # Unknown character in board character_set = set() for row in minefield: character_set.update(row) - if character_set - set(' *'): - raise ValueError('The board is invalid with current input.') + if character_set - set(" *"): + raise ValueError("The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py/minesweeper/tests.py b/evaluator/datasets/polyglot_py/minesweeper/tests.py index 75e46a32d..5334da7d1 100644 --- a/evaluator/datasets/polyglot_py/minesweeper/tests.py +++ b/evaluator/datasets/polyglot_py/minesweeper/tests.py @@ -63,14 +63,10 @@ def test_different_len(self): with self.assertRaises(ValueError) as err: annotate([" ", "* ", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") def test_invalid_char(self): with self.assertRaises(ValueError) as err: annotate(["X * "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py/nth-prime/solution.py b/evaluator/datasets/polyglot_py/nth-prime/solution.py index 73517c4b3..1f6c10df6 100644 --- a/evaluator/datasets/polyglot_py/nth-prime/solution.py +++ b/evaluator/datasets/polyglot_py/nth-prime/solution.py @@ -4,7 +4,7 @@ def prime(number): if number < 1: - raise ValueError('there is no zeroth prime') + raise ValueError("there is no zeroth prime") known = [] candidates = prime_candidates() diff --git a/evaluator/datasets/polyglot_py/ocr-numbers/main.py b/evaluator/datasets/polyglot_py/ocr-numbers/main.py index 931e5b849..a86d4c17f 100644 --- a/evaluator/datasets/polyglot_py/ocr-numbers/main.py +++ b/evaluator/datasets/polyglot_py/ocr-numbers/main.py @@ -1,3 +1,2 @@ def convert(input_grid: list[str]) -> str: pass - diff --git a/evaluator/datasets/polyglot_py/ocr-numbers/solution.py b/evaluator/datasets/polyglot_py/ocr-numbers/solution.py index bc926487e..1dfbd4dea 100644 --- a/evaluator/datasets/polyglot_py/ocr-numbers/solution.py +++ b/evaluator/datasets/polyglot_py/ocr-numbers/solution.py @@ -3,41 +3,43 @@ def split_ocr(ocr): - return [[ocr[idx][NUM_COLS * jam:NUM_COLS * (jam + 1)] for idx in range(NUM_ROWS)] - for jam in range(len(ocr[0]) // NUM_COLS)] + return [ + [ocr[idx][NUM_COLS * jam : NUM_COLS * (jam + 1)] for idx in range(NUM_ROWS)] + for jam in range(len(ocr[0]) // NUM_COLS) + ] -ALL = [' _ _ _ _ _ _ _ _ ', - ' | _| _||_||_ |_ ||_||_|| |', - ' ||_ _| | _||_| ||_| _||_|', - ' '] +ALL = [ + " _ _ _ _ _ _ _ _ ", + " | _| _||_||_ |_ ||_||_|| |", + " ||_ _| | _||_| ||_| _||_|", + " ", +] OCR_LIST = split_ocr(ALL) OCR_LIST = [OCR_LIST[-1]] + OCR_LIST[:9] def convert(input_grid): - split_indices = (list(range(0, len(input_grid), NUM_ROWS)) + - [len(input_grid)]) + split_indices = list(range(0, len(input_grid), NUM_ROWS)) + [len(input_grid)] - lines = [input_grid[start:end] - for start, end in zip(split_indices[:-1], split_indices[1:])] + lines = [input_grid[start:end] for start, end in zip(split_indices[:-1], split_indices[1:])] - return ','.join(convert_one_line(line) for line in lines) + return ",".join(convert_one_line(line) for line in lines) def convert_one_line(input_grid): if len(input_grid) != NUM_ROWS: - raise ValueError('Number of input lines is not a multiple of four') + raise ValueError("Number of input lines is not a multiple of four") if len(input_grid[0]) % NUM_COLS: - raise ValueError('Number of input columns is not a multiple of three') + raise ValueError("Number of input columns is not a multiple of three") numbers = split_ocr(input_grid) - digits = '' + digits = "" for num in numbers: try: digits += str(OCR_LIST.index(num)) except ValueError: - digits += '?' + digits += "?" return digits diff --git a/evaluator/datasets/polyglot_py/ocr-numbers/tests.py b/evaluator/datasets/polyglot_py/ocr-numbers/tests.py index ee5df73b6..a934da2f2 100644 --- a/evaluator/datasets/polyglot_py/ocr-numbers/tests.py +++ b/evaluator/datasets/polyglot_py/ocr-numbers/tests.py @@ -25,9 +25,7 @@ def test_input_with_a_number_of_lines_that_is_not_a_multiple_of_four_raises_an_e with self.assertRaises(ValueError) as err: convert([" _ ", "| |", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "Number of input lines is not a multiple of four" - ) + self.assertEqual(err.exception.args[0], "Number of input lines is not a multiple of four") def test_input_with_a_number_of_columns_that_is_not_a_multiple_of_three_raises_an_error( self, @@ -35,9 +33,7 @@ def test_input_with_a_number_of_columns_that_is_not_a_multiple_of_three_raises_a with self.assertRaises(ValueError) as err: convert([" ", " |", " |", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "Number of input columns is not a multiple of three" - ) + self.assertEqual(err.exception.args[0], "Number of input columns is not a multiple of three") def test_recognizes_110101100(self): self.assertEqual( diff --git a/evaluator/datasets/polyglot_py/octal/solution.py b/evaluator/datasets/polyglot_py/octal/solution.py index 86000ee4d..9da7b560d 100644 --- a/evaluator/datasets/polyglot_py/octal/solution.py +++ b/evaluator/datasets/polyglot_py/octal/solution.py @@ -1,11 +1,10 @@ def parse_octal(digits): digits = _validate_octal(digits) - return sum(int(digit) * 8 ** idx - for (idx, digit) in enumerate(reversed(digits))) + return sum(int(digit) * 8**idx for (idx, digit) in enumerate(reversed(digits))) def _validate_octal(digits): for digit in digits: - if not '0' <= digit < '8': + if not "0" <= digit < "8": raise ValueError("Invalid octal digit: " + digit) return digits diff --git a/evaluator/datasets/polyglot_py/octal/tests.py b/evaluator/datasets/polyglot_py/octal/tests.py index f53a4913b..4514d8fb1 100644 --- a/evaluator/datasets/polyglot_py/octal/tests.py +++ b/evaluator/datasets/polyglot_py/octal/tests.py @@ -4,6 +4,7 @@ If the string supplied to parse_octal cannot be parsed as an octal number your program should raise a ValueError with a meaningful error message. """ + import unittest from main import parse_octal @@ -48,5 +49,5 @@ def assertRaisesWithMessage(self, exception): return self.assertRaisesRegex(exception, r".+") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/paasio/tests.py b/evaluator/datasets/polyglot_py/paasio/tests.py index e2df06bab..383c25e1c 100644 --- a/evaluator/datasets/polyglot_py/paasio/tests.py +++ b/evaluator/datasets/polyglot_py/paasio/tests.py @@ -1,13 +1,12 @@ import errno -import os -import unittest import inspect import io -from unittest.mock import ANY, call, NonCallableMagicMock, patch +import os +import unittest +from unittest.mock import ANY, NonCallableMagicMock, call, patch from main import MeteredFile, MeteredSocket - ZEN = b"""Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. @@ -89,9 +88,7 @@ def recv(self, bufsize, flags=0): if bufsize is None: raise TypeError("'NoneType' object cannot be interpreted as an integer") if not isinstance(flags, int): - raise TypeError( - "an integer is required (got type {})".format(type(flags).__name__) - ) + raise TypeError("an integer is required (got type {})".format(type(flags).__name__)) self.flags = flags if self.__exception is not None: raise self.__exception @@ -104,9 +101,7 @@ def send(self, data, flags=0): if self.__closed: raise OSError(errno.EBADF, os.strerror(errno.EBADF)) if not isinstance(flags, int): - raise TypeError( - "an integer is required (got type {})".format(type(flags).__name__) - ) + raise TypeError("an integer is required (got type {})".format(type(flags).__name__)) self.flags = flags if self.__chunk is None: return self._sender.write(data) @@ -133,14 +128,13 @@ def __call__(self, *args, **kwargs): return self.mock_object def __repr__(self): - return "".format( - hex(id(self)), self.mock_object - ) + return "".format(hex(id(self)), self.mock_object) mock_object = None init_called = 0 initialized = False + class PaasioTest(unittest.TestCase): def test_meteredsocket_context_manager(self): wrapped = MockSock() @@ -282,10 +276,7 @@ def test_meteredsocket_bufsize_required(self): with self.assertRaisesRegex(TypeError, "^'NoneType'.+integer$"): with MeteredSocket(mock) as socket: socket.recv(None) - self.assertTrue( - call(None) in mock.recv.mock_calls - or call(None, ANY) in mock.recv.mock_calls - ) + self.assertTrue(call(None) in mock.recv.mock_calls or call(None, ANY) in mock.recv.mock_calls) def test_meteredsocket_flags_support(self): mock = NonCallableMagicMock(wraps=MockSock(), autospec=True) @@ -408,9 +399,7 @@ def test_meteredfile_iteration(self, super_mock): for line in file: actual_reads += line self.assertLess(0, mock.readline.call_count, "File's readline not called") - self.assertGreater( - 50, mock.readline.call_count, "Possible infinte loop detected" - ) + self.assertGreater(50, mock.readline.call_count, "Possible infinte loop detected") self.assertEqual(file.read_ops, mock.readline.call_count) self.assertFalse(mock.__iter__.called) self.assertEqual(len(ZEN), file.read_bytes) diff --git a/evaluator/datasets/polyglot_py/palindrome-products/main.py b/evaluator/datasets/polyglot_py/palindrome-products/main.py index e50eeec17..1c62da853 100644 --- a/evaluator/datasets/polyglot_py/palindrome-products/main.py +++ b/evaluator/datasets/polyglot_py/palindrome-products/main.py @@ -1,4 +1,5 @@ -from typing import Tuple, List, Optional +from typing import List, Optional, Tuple + def largest(min_factor: int, max_factor: int) -> Tuple[Optional[int], List[Tuple[int, int]]]: """Given a range of numbers, find the largest palindromes which diff --git a/evaluator/datasets/polyglot_py/palindrome-products/solution.py b/evaluator/datasets/polyglot_py/palindrome-products/solution.py index f656ff185..6c91203d3 100644 --- a/evaluator/datasets/polyglot_py/palindrome-products/solution.py +++ b/evaluator/datasets/polyglot_py/palindrome-products/solution.py @@ -1,24 +1,23 @@ from itertools import chain -from math import log10, floor, ceil +from math import ceil, floor, log10 def largest(min_factor, max_factor): - return get_extreme_palindrome_with_factors(max_factor, min_factor, 'largest') + return get_extreme_palindrome_with_factors(max_factor, min_factor, "largest") def smallest(max_factor, min_factor): - return get_extreme_palindrome_with_factors(max_factor, min_factor, 'smallest') + return get_extreme_palindrome_with_factors(max_factor, min_factor, "smallest") def get_extreme_palindrome_with_factors(max_factor, min_factor, extreme): - palindromes_found = palindromes(max_factor, min_factor, reverse=(extreme == 'largest')) + palindromes_found = palindromes(max_factor, min_factor, reverse=(extreme == "largest")) factor_pairs = None for palindrome in palindromes_found: - factor_pairs = ((factor, palindrome // factor) - for factor in range(min_factor, max_factor + 1) - if palindrome % factor == 0) - factor_pairs = list(pair for pair in factor_pairs - if min_factor <= pair[1] <= max_factor) + factor_pairs = ( + (factor, palindrome // factor) for factor in range(min_factor, max_factor + 1) if palindrome % factor == 0 + ) + factor_pairs = list(pair for pair in factor_pairs if min_factor <= pair[1] <= max_factor) if len(factor_pairs) > 0: break @@ -32,7 +31,7 @@ def reverse_num(number): reversed_nums = 0 while number > 0: reversed_nums *= 10 - reversed_nums += (number % 10) + reversed_nums += number % 10 number //= 10 return reversed_nums @@ -49,10 +48,10 @@ def palindromes(max_factor, min_factor, reverse=False): most of the palindromes just to find the one it needs. """ if max_factor < min_factor: - raise ValueError('min must be <= max') + raise ValueError("min must be <= max") - minimum = min_factor ** 2 - maximum = max_factor ** 2 + minimum = min_factor**2 + maximum = max_factor**2 def gen_palindromes_of_length(digit_count, reverse=reverse): """Generates all palindromes with `nd` number of digits that are @@ -60,19 +59,15 @@ def gen_palindromes_of_length(digit_count, reverse=reverse): Again, if `reverse` is True, the palindromes are generated in reverse order. """ - even_nd = (digit_count % 2 == 0) + even_nd = digit_count % 2 == 0 - min_left_half = max(10 ** (int(ceil(digit_count / 2)) - 1), - minimum // (10 ** (digit_count // 2))) - max_left_half = min((10 ** int(ceil(digit_count / 2))) - 1, - maximum // (10 ** (digit_count // 2))) + min_left_half = max(10 ** (int(ceil(digit_count / 2)) - 1), minimum // (10 ** (digit_count // 2))) + max_left_half = min((10 ** int(ceil(digit_count / 2))) - 1, maximum // (10 ** (digit_count // 2))) current_left_half = min_left_half if not reverse else max_left_half def make_palindrome(left_half, even_nd=False): - right_half = (reverse_num(left_half) - if even_nd - else reverse_num(left_half // 10)) + right_half = reverse_num(left_half) if even_nd else reverse_num(left_half // 10) return (left_half * (10 ** (digit_count // 2))) + right_half if not reverse: @@ -101,8 +96,6 @@ def make_palindrome(left_half, even_nd=False): min_nd = num_digits(minimum) max_nd = num_digits(maximum) - lengths = (range(min_nd, max_nd + 1) - if not reverse - else range(max_nd, min_nd - 1, -1)) + lengths = range(min_nd, max_nd + 1) if not reverse else range(max_nd, min_nd - 1, -1) return chain(*map(gen_palindromes_of_length, lengths)) diff --git a/evaluator/datasets/polyglot_py/pangram/tests.py b/evaluator/datasets/polyglot_py/pangram/tests.py index 316ff88da..ae8e0df5d 100644 --- a/evaluator/datasets/polyglot_py/pangram/tests.py +++ b/evaluator/datasets/polyglot_py/pangram/tests.py @@ -32,9 +32,7 @@ def test_with_underscores(self): self.assertIs(is_pangram("the_quick_brown_fox_jumps_over_the_lazy_dog"), True) def test_with_numbers(self): - self.assertIs( - is_pangram("the 1 quick brown fox jumps over the 2 lazy dogs"), True - ) + self.assertIs(is_pangram("the 1 quick brown fox jumps over the 2 lazy dogs"), True) def test_missing_letters_replaced_by_numbers(self): self.assertIs(is_pangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"), False) diff --git a/evaluator/datasets/polyglot_py/pascals-triangle/main.py b/evaluator/datasets/polyglot_py/pascals-triangle/main.py index 9dccd950c..90289a64d 100644 --- a/evaluator/datasets/polyglot_py/pascals-triangle/main.py +++ b/evaluator/datasets/polyglot_py/pascals-triangle/main.py @@ -1,4 +1,5 @@ from typing import List + def rows(row_count: int) -> List[List[int]]: pass diff --git a/evaluator/datasets/polyglot_py/pascals-triangle/tests.py b/evaluator/datasets/polyglot_py/pascals-triangle/tests.py index 0e9f238cc..e8a5434af 100644 --- a/evaluator/datasets/polyglot_py/pascals-triangle/tests.py +++ b/evaluator/datasets/polyglot_py/pascals-triangle/tests.py @@ -59,6 +59,4 @@ def test_solution_is_recursive(self): with self.assertRaises(RecursionError) as err: rows(sys.getrecursionlimit() + 10) self.assertEqual(type(err.exception), RecursionError) - self.assertEqual( - err.exception.args[0][:32], "maximum recursion depth exceeded" - ) + self.assertEqual(err.exception.args[0][:32], "maximum recursion depth exceeded") diff --git a/evaluator/datasets/polyglot_py/perfect-numbers/main.py b/evaluator/datasets/polyglot_py/perfect-numbers/main.py index 867d2d1db..2673dac99 100644 --- a/evaluator/datasets/polyglot_py/perfect-numbers/main.py +++ b/evaluator/datasets/polyglot_py/perfect-numbers/main.py @@ -1,5 +1,5 @@ def classify(number: int) -> str: - """ A perfect number equals the sum of its positive divisors. + """A perfect number equals the sum of its positive divisors. :param number: int a positive integer :return: str the classification of the input integer diff --git a/evaluator/datasets/polyglot_py/perfect-numbers/solution.py b/evaluator/datasets/polyglot_py/perfect-numbers/solution.py index 3a3494817..fff76c259 100644 --- a/evaluator/datasets/polyglot_py/perfect-numbers/solution.py +++ b/evaluator/datasets/polyglot_py/perfect-numbers/solution.py @@ -1,5 +1,6 @@ import math + def divisor_generator(number): """Returns an unordered list of divisors for n (1 < number). @@ -15,20 +16,20 @@ def divisor_generator(number): def classify(number): - """ A perfect number equals the sum of its positive divisors. + """A perfect number equals the sum of its positive divisors. :param number: int a positive integer :return: str the classification of the input integer """ if number <= 0: - raise ValueError('Classification is only possible for positive integers.') + raise ValueError("Classification is only possible for positive integers.") aliquot_sum = sum(divisor_generator(number)) + (1 if number > 1 else 0) if aliquot_sum < number: - return 'deficient' + return "deficient" elif aliquot_sum == number: - return 'perfect' + return "perfect" else: - return 'abundant' + return "abundant" diff --git a/evaluator/datasets/polyglot_py/phone-number/main.py b/evaluator/datasets/polyglot_py/phone-number/main.py index 35854b83f..406abe9bd 100644 --- a/evaluator/datasets/polyglot_py/phone-number/main.py +++ b/evaluator/datasets/polyglot_py/phone-number/main.py @@ -1,9 +1,9 @@ class PhoneNumber: - number: str # XXXXXXXXXX - area_code: str # XXX - + number: str # XXXXXXXXXX + area_code: str # XXX + def __init__(self, number: str): pass - def pretty(self) -> str: # (XXX)-XXX-XXXX + def pretty(self) -> str: # (XXX)-XXX-XXXX pass diff --git a/evaluator/datasets/polyglot_py/phone-number/solution.py b/evaluator/datasets/polyglot_py/phone-number/solution.py index d23102a01..58b59f39b 100644 --- a/evaluator/datasets/polyglot_py/phone-number/solution.py +++ b/evaluator/datasets/polyglot_py/phone-number/solution.py @@ -10,44 +10,44 @@ def __init__(self, number): self.subscriber_number = self.number[-4:] def pretty(self): - return f'({self.area_code})-{self.exchange_code}-{self.subscriber_number}' + return f"({self.area_code})-{self.exchange_code}-{self.subscriber_number}" def _clean(self, number): - preprocess = re.sub(r'[() +-.]', '', number) + preprocess = re.sub(r"[() +-.]", "", number) if any(item for item in preprocess if item.isalpha()): - raise ValueError('letters not permitted') + raise ValueError("letters not permitted") if any(item for item in preprocess if item in punctuation): - raise ValueError('punctuations not permitted') + raise ValueError("punctuations not permitted") return self._normalize(preprocess) def _normalize(self, number): if len(number) < 10: - raise ValueError('must not be fewer than 10 digits') + raise ValueError("must not be fewer than 10 digits") if len(number) > 11: - raise ValueError('must not be greater than 11 digits') - - if len(number) == 10 or len(number) == 11 and number.startswith('1'): - if number[-10] == '0': - raise ValueError('area code cannot start with zero') - elif number[-10] == '1': - raise ValueError('area code cannot start with one') - elif number[-7] == '0': - raise ValueError('exchange code cannot start with zero') - elif number[-7] == '1': - raise ValueError('exchange code cannot start with one') + raise ValueError("must not be greater than 11 digits") + + if len(number) == 10 or len(number) == 11 and number.startswith("1"): + if number[-10] == "0": + raise ValueError("area code cannot start with zero") + elif number[-10] == "1": + raise ValueError("area code cannot start with one") + elif number[-7] == "0": + raise ValueError("exchange code cannot start with zero") + elif number[-7] == "1": + raise ValueError("exchange code cannot start with one") else: - valid = number[-10] in '23456789' and number[-7] in '23456789' + valid = number[-10] in "23456789" and number[-7] in "23456789" else: valid = False - if number[0] in '023456789': - raise ValueError('11 digits must start with 1') + if number[0] in "023456789": + raise ValueError("11 digits must start with 1") if valid: return number[-10:] - return None # [Pylint]: R1710; + return None # [Pylint]: R1710; diff --git a/evaluator/datasets/polyglot_py/pig-latin/solution.py b/evaluator/datasets/polyglot_py/pig-latin/solution.py index f4b5ca575..4461199f6 100644 --- a/evaluator/datasets/polyglot_py/pig-latin/solution.py +++ b/evaluator/datasets/polyglot_py/pig-latin/solution.py @@ -1,8 +1,7 @@ import re - -re_cons = re.compile('^([^aeiou]?qu|[^aeiouy]+|y(?=[aeiou]))([a-z]*)') -re_vowel = re.compile('^([aeiou]|y[^aeiou]|xr)[a-z]*') +re_cons = re.compile("^([^aeiou]?qu|[^aeiouy]+|y(?=[aeiou]))([a-z]*)") +re_vowel = re.compile("^([aeiou]|y[^aeiou]|xr)[a-z]*") def split_initial_consonant_sound(word): @@ -17,8 +16,8 @@ def translate(text): words = [] for word in text.split(): if starts_with_vowel_sound(word): - words.append(word + 'ay') + words.append(word + "ay") else: head, tail = split_initial_consonant_sound(word) - words.append(tail + head + 'ay') - return ' '.join(words) + words.append(tail + head + "ay") + return " ".join(words) diff --git a/evaluator/datasets/polyglot_py/point-mutations/tests.py b/evaluator/datasets/polyglot_py/point-mutations/tests.py index 4c26d554e..31c62e5e2 100644 --- a/evaluator/datasets/polyglot_py/point-mutations/tests.py +++ b/evaluator/datasets/polyglot_py/point-mutations/tests.py @@ -5,32 +5,29 @@ class PointMutationsTest(unittest.TestCase): def test_no_difference_between_empty_strands(self): - self.assertEqual(hamming_distance('', ''), 0) + self.assertEqual(hamming_distance("", ""), 0) def test_no_difference_between_identical_strands(self): - self.assertEqual(hamming_distance('GGACTGA', 'GGACTGA'), 0) + self.assertEqual(hamming_distance("GGACTGA", "GGACTGA"), 0) def test_complete_hamming_distance_in_small_strand(self): - self.assertEqual(hamming_distance('ACT', 'GGA'), 3) + self.assertEqual(hamming_distance("ACT", "GGA"), 3) def test_hamming_distance_in_off_by_one_strand(self): - self.assertEqual( - hamming_distance('GGACGGATTCTGACCTGGACTAATTTTGGGG', - 'AGGACGGATTCTGACCTGGACTAATTTTGGGG'), 19) + self.assertEqual(hamming_distance("GGACGGATTCTGACCTGGACTAATTTTGGGG", "AGGACGGATTCTGACCTGGACTAATTTTGGGG"), 19) def test_small_hamming_distance_in_middle_somewhere(self): - self.assertEqual(hamming_distance('GGACG', 'GGTCG'), 1) + self.assertEqual(hamming_distance("GGACG", "GGTCG"), 1) def test_larger_distance(self): - self.assertEqual(hamming_distance('ACCAGGG', 'ACTATGG'), 2) + self.assertEqual(hamming_distance("ACCAGGG", "ACTATGG"), 2) def test_ignores_extra_length_on_other_strand_when_longer(self): - self.assertEqual(hamming_distance('AAACTAGGGG', 'AGGCTAGCGGTAGGAC'), 3) + self.assertEqual(hamming_distance("AAACTAGGGG", "AGGCTAGCGGTAGGAC"), 3) def test_ignores_extra_length_on_original_strand_when_longer(self): - self.assertEqual( - hamming_distance('GACTACGGACAGGGTAGGGAAT', 'GACATCGCACACC'), 5) + self.assertEqual(hamming_distance("GACTACGGACAGGGTAGGGAAT", "GACATCGCACACC"), 5) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/poker/solution.py b/evaluator/datasets/polyglot_py/poker/solution.py index e650f143c..e0556061b 100644 --- a/evaluator/datasets/polyglot_py/poker/solution.py +++ b/evaluator/datasets/polyglot_py/poker/solution.py @@ -15,8 +15,8 @@ def allmax(iterable, key=None): def hand_rank(hand): - hand = hand.replace('10', 'T').split() - card_ranks = ['..23456789TJQKA'.index(idx) for idx, _ in hand] + hand = hand.replace("10", "T").split() + card_ranks = ["..23456789TJQKA".index(idx) for idx, _ in hand] groups = [(card_ranks.count(idx), idx) for idx in set(card_ranks)] groups.sort(reverse=True) counts, ranks = zip(*groups) @@ -24,13 +24,25 @@ def hand_rank(hand): ranks = (5, 4, 3, 2, 1) straight = (len(counts) == 5) and (max(ranks) - min(ranks) == 4) flush = len({idx for _, idx in hand}) == 1 - return (9 if counts == (5,) else - 8 if straight and flush else - 7 if counts == (4, 1) else - 6 if counts == (3, 2) else - 5 if flush else - 4 if straight else - 3 if counts == (3, 1, 1) else - 2 if counts == (2, 2, 1) else - 1 if counts == (2, 1, 1, 1) else - 0, ranks) + return ( + 9 + if counts == (5,) + else 8 + if straight and flush + else 7 + if counts == (4, 1) + else 6 + if counts == (3, 2) + else 5 + if flush + else 4 + if straight + else 3 + if counts == (3, 1, 1) + else 2 + if counts == (2, 2, 1) + else 1 + if counts == (2, 1, 1, 1) + else 0, + ranks, + ) diff --git a/evaluator/datasets/polyglot_py/poker/tests.py b/evaluator/datasets/polyglot_py/poker/tests.py index d36b5e5a9..aae0182bb 100644 --- a/evaluator/datasets/polyglot_py/poker/tests.py +++ b/evaluator/datasets/polyglot_py/poker/tests.py @@ -35,189 +35,121 @@ def test_a_tie_has_multiple_winners(self): def test_multiple_hands_with_the_same_high_cards_tie_compares_next_highest_ranked_down_to_last_card( self, ): - self.assertEqual( - best_hands(["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"]), ["3S 5H 6S 8D 7H"] - ) + self.assertEqual(best_hands(["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"]), ["3S 5H 6S 8D 7H"]) def test_winning_high_card_hand_also_has_the_lowest_card(self): - self.assertEqual( - best_hands(["2S 5H 6S 8D 7H", "3S 4D 6D 8C 7S"]), ["2S 5H 6S 8D 7H"] - ) + self.assertEqual(best_hands(["2S 5H 6S 8D 7H", "3S 4D 6D 8C 7S"]), ["2S 5H 6S 8D 7H"]) def test_one_pair_beats_high_card(self): - self.assertEqual( - best_hands(["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"]), ["2S 4H 6S 4D JH"] - ) + self.assertEqual(best_hands(["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"]), ["2S 4H 6S 4D JH"]) def test_highest_pair_wins(self): - self.assertEqual( - best_hands(["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"]), ["2S 4H 6C 4D JD"] - ) + self.assertEqual(best_hands(["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"]), ["2S 4H 6C 4D JD"]) def test_both_hands_have_the_same_pair_high_card_wins(self): - self.assertEqual( - best_hands(["4H 4S AH JC 3D", "4C 4D AS 5D 6C"]), ["4H 4S AH JC 3D"] - ) + self.assertEqual(best_hands(["4H 4S AH JC 3D", "4C 4D AS 5D 6C"]), ["4H 4S AH JC 3D"]) def test_two_pairs_beats_one_pair(self): - self.assertEqual( - best_hands(["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"]), ["4S 5H 4C 8C 5C"] - ) + self.assertEqual(best_hands(["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"]), ["4S 5H 4C 8C 5C"]) def test_both_hands_have_two_pairs_highest_ranked_pair_wins(self): - self.assertEqual( - best_hands(["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"]), ["2S 8H 2D 8D 3H"] - ) + self.assertEqual(best_hands(["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"]), ["2S 8H 2D 8D 3H"]) def test_both_hands_have_two_pairs_with_the_same_highest_ranked_pair_tie_goes_to_low_pair( self, ): - self.assertEqual( - best_hands(["2S QS 2C QD JH", "JD QH JS 8D QC"]), ["JD QH JS 8D QC"] - ) + self.assertEqual(best_hands(["2S QS 2C QD JH", "JD QH JS 8D QC"]), ["JD QH JS 8D QC"]) def test_both_hands_have_two_identically_ranked_pairs_tie_goes_to_remaining_card_kicker( self, ): - self.assertEqual( - best_hands(["JD QH JS 8D QC", "JS QS JC 2D QD"]), ["JD QH JS 8D QC"] - ) + self.assertEqual(best_hands(["JD QH JS 8D QC", "JS QS JC 2D QD"]), ["JD QH JS 8D QC"]) def test_both_hands_have_two_pairs_that_add_to_the_same_value_win_goes_to_highest_pair( self, ): - self.assertEqual( - best_hands(["6S 6H 3S 3H AS", "7H 7S 2H 2S AC"]), ["7H 7S 2H 2S AC"] - ) + self.assertEqual(best_hands(["6S 6H 3S 3H AS", "7H 7S 2H 2S AC"]), ["7H 7S 2H 2S AC"]) def test_two_pairs_first_ranked_by_largest_pair(self): - self.assertEqual( - best_hands(["5C 2S 5S 4H 4C", "6S 2S 6H 7C 2C"]), ["6S 2S 6H 7C 2C"] - ) + self.assertEqual(best_hands(["5C 2S 5S 4H 4C", "6S 2S 6H 7C 2C"]), ["6S 2S 6H 7C 2C"]) def test_three_of_a_kind_beats_two_pair(self): - self.assertEqual( - best_hands(["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"]), ["4S 5H 4C 8S 4H"] - ) + self.assertEqual(best_hands(["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"]), ["4S 5H 4C 8S 4H"]) def test_both_hands_have_three_of_a_kind_tie_goes_to_highest_ranked_triplet(self): - self.assertEqual( - best_hands(["2S 2H 2C 8D JH", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"] - ) + self.assertEqual(best_hands(["2S 2H 2C 8D JH", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"]) def test_with_multiple_decks_two_players_can_have_same_three_of_a_kind_ties_go_to_highest_remaining_cards( self, ): - self.assertEqual( - best_hands(["5S AH AS 7C AD", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"] - ) + self.assertEqual(best_hands(["5S AH AS 7C AD", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"]) def test_a_straight_beats_three_of_a_kind(self): - self.assertEqual( - best_hands(["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"]), ["3S 4D 2S 6D 5C"] - ) + self.assertEqual(best_hands(["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"]), ["3S 4D 2S 6D 5C"]) def test_aces_can_end_a_straight_10_j_q_k_a(self): - self.assertEqual( - best_hands(["4S 5H 4C 8D 4H", "10D JH QS KD AC"]), ["10D JH QS KD AC"] - ) + self.assertEqual(best_hands(["4S 5H 4C 8D 4H", "10D JH QS KD AC"]), ["10D JH QS KD AC"]) def test_aces_can_start_a_straight_a_2_3_4_5(self): - self.assertEqual( - best_hands(["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"]), ["4D AH 3S 2D 5C"] - ) + self.assertEqual(best_hands(["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"]), ["4D AH 3S 2D 5C"]) def test_aces_cannot_be_in_the_middle_of_a_straight_q_k_a_2_3(self): - self.assertEqual( - best_hands(["2C 3D 7H 5H 2S", "QS KH AC 2D 3S"]), ["2C 3D 7H 5H 2S"] - ) + self.assertEqual(best_hands(["2C 3D 7H 5H 2S", "QS KH AC 2D 3S"]), ["2C 3D 7H 5H 2S"]) def test_both_hands_with_a_straight_tie_goes_to_highest_ranked_card(self): - self.assertEqual( - best_hands(["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"]), ["5S 7H 8S 9D 6H"] - ) + self.assertEqual(best_hands(["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"]), ["5S 7H 8S 9D 6H"]) def test_even_though_an_ace_is_usually_high_a_5_high_straight_is_the_lowest_scoring_straight( self, ): - self.assertEqual( - best_hands(["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"]), ["2H 3C 4D 5D 6H"] - ) + self.assertEqual(best_hands(["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"]), ["2H 3C 4D 5D 6H"]) def test_flush_beats_a_straight(self): - self.assertEqual( - best_hands(["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"]), ["2S 4S 5S 6S 7S"] - ) + self.assertEqual(best_hands(["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"]), ["2S 4S 5S 6S 7S"]) def test_both_hands_have_a_flush_tie_goes_to_high_card_down_to_the_last_one_if_necessary( self, ): - self.assertEqual( - best_hands(["2H 7H 8H 9H 6H", "3S 5S 6S 7S 8S"]), ["2H 7H 8H 9H 6H"] - ) + self.assertEqual(best_hands(["2H 7H 8H 9H 6H", "3S 5S 6S 7S 8S"]), ["2H 7H 8H 9H 6H"]) def test_full_house_beats_a_flush(self): - self.assertEqual( - best_hands(["3H 6H 7H 8H 5H", "4S 5H 4C 5D 4H"]), ["4S 5H 4C 5D 4H"] - ) + self.assertEqual(best_hands(["3H 6H 7H 8H 5H", "4S 5H 4C 5D 4H"]), ["4S 5H 4C 5D 4H"]) def test_both_hands_have_a_full_house_tie_goes_to_highest_ranked_triplet(self): - self.assertEqual( - best_hands(["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 8S 8D"] - ) + self.assertEqual(best_hands(["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 8S 8D"]) def test_with_multiple_decks_both_hands_have_a_full_house_with_the_same_triplet_tie_goes_to_the_pair( self, ): - self.assertEqual( - best_hands(["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 9S 9D"] - ) + self.assertEqual(best_hands(["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 9S 9D"]) def test_four_of_a_kind_beats_a_full_house(self): - self.assertEqual( - best_hands(["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"]), ["3S 3H 2S 3D 3C"] - ) + self.assertEqual(best_hands(["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"]), ["3S 3H 2S 3D 3C"]) def test_both_hands_have_four_of_a_kind_tie_goes_to_high_quad(self): - self.assertEqual( - best_hands(["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"]), ["4S 5H 5S 5D 5C"] - ) + self.assertEqual(best_hands(["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"]), ["4S 5H 5S 5D 5C"]) def test_with_multiple_decks_both_hands_with_identical_four_of_a_kind_tie_determined_by_kicker( self, ): - self.assertEqual( - best_hands(["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"]), ["3S 3H 4S 3D 3C"] - ) + self.assertEqual(best_hands(["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"]), ["3S 3H 4S 3D 3C"]) def test_straight_flush_beats_four_of_a_kind(self): - self.assertEqual( - best_hands(["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"]), ["7S 8S 9S 6S 10S"] - ) + self.assertEqual(best_hands(["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"]), ["7S 8S 9S 6S 10S"]) def test_aces_can_end_a_straight_flush_10_j_q_k_a(self): - self.assertEqual( - best_hands(["KC AH AS AD AC", "10C JC QC KC AC"]), ["10C JC QC KC AC"] - ) + self.assertEqual(best_hands(["KC AH AS AD AC", "10C JC QC KC AC"]), ["10C JC QC KC AC"]) def test_aces_can_start_a_straight_flush_a_2_3_4_5(self): - self.assertEqual( - best_hands(["KS AH AS AD AC", "4H AH 3H 2H 5H"]), ["4H AH 3H 2H 5H"] - ) + self.assertEqual(best_hands(["KS AH AS AD AC", "4H AH 3H 2H 5H"]), ["4H AH 3H 2H 5H"]) def test_aces_cannot_be_in_the_middle_of_a_straight_flush_q_k_a_2_3(self): - self.assertEqual( - best_hands(["2C AC QC 10C KC", "QH KH AH 2H 3H"]), ["2C AC QC 10C KC"] - ) + self.assertEqual(best_hands(["2C AC QC 10C KC", "QH KH AH 2H 3H"]), ["2C AC QC 10C KC"]) def test_both_hands_have_a_straight_flush_tie_goes_to_highest_ranked_card(self): - self.assertEqual( - best_hands(["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"]), ["5S 7S 8S 9S 6S"] - ) + self.assertEqual(best_hands(["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"]), ["5S 7S 8S 9S 6S"]) def test_even_though_an_ace_is_usually_high_a_5_high_straight_flush_is_the_lowest_scoring_straight_flush( self, ): - self.assertEqual( - best_hands(["2H 3H 4H 5H 6H", "4D AD 3D 2D 5D"]), ["2H 3H 4H 5H 6H"] - ) + self.assertEqual(best_hands(["2H 3H 4H 5H 6H", "4D AD 3D 2D 5D"]), ["2H 3H 4H 5H 6H"]) diff --git a/evaluator/datasets/polyglot_py/pov/solution.py b/evaluator/datasets/polyglot_py/pov/solution.py index febb8fd8d..d3f4c2663 100644 --- a/evaluator/datasets/polyglot_py/pov/solution.py +++ b/evaluator/datasets/polyglot_py/pov/solution.py @@ -57,15 +57,13 @@ def from_pov(self, from_node): for child in tree.children: stack.append(child.add(tree.remove(child.label))) - raise ValueError('Tree could not be reoriented') - - + raise ValueError("Tree could not be reoriented") def path_to(self, from_node, to_node): try: reordered = self.from_pov(from_node) except ValueError: - raise ValueError('No path found') + raise ValueError("No path found") stack = reordered.children path = [from_node] @@ -73,7 +71,7 @@ def path_to(self, from_node, to_node): try: tree = stack.pop() except IndexError as error: - raise ValueError('No path found') from error + raise ValueError("No path found") from error if to_node in tree: path.append(tree.label) stack = tree.children diff --git a/evaluator/datasets/polyglot_py/prime-factors/main.py b/evaluator/datasets/polyglot_py/prime-factors/main.py index 068f0cac4..4ecb5bb2d 100644 --- a/evaluator/datasets/polyglot_py/prime-factors/main.py +++ b/evaluator/datasets/polyglot_py/prime-factors/main.py @@ -1,4 +1,5 @@ from typing import List + def factors(value: int) -> List[int]: pass diff --git a/evaluator/datasets/polyglot_py/protein-translation/solution.py b/evaluator/datasets/polyglot_py/protein-translation/solution.py index e8c2ac29b..b17f4c36b 100644 --- a/evaluator/datasets/polyglot_py/protein-translation/solution.py +++ b/evaluator/datasets/polyglot_py/protein-translation/solution.py @@ -1,25 +1,38 @@ -CODONS = {'AUG': 'Methionine', 'UUU': 'Phenylalanine', - 'UUC': 'Phenylalanine', 'UUA': 'Leucine', 'UUG': 'Leucine', - 'UCU': 'Serine', 'UCC': 'Serine', 'UCA': 'Serine', - 'UCG': 'Serine', 'UAU': 'Tyrosine', 'UAC': 'Tyrosine', - 'UGU': 'Cysteine', 'UGC': 'Cysteine', 'UGG': 'Tryptophan', - 'UAA': 'STOP', 'UAG': 'STOP', 'UGA': 'STOP'} +CODONS = { + "AUG": "Methionine", + "UUU": "Phenylalanine", + "UUC": "Phenylalanine", + "UUA": "Leucine", + "UUG": "Leucine", + "UCU": "Serine", + "UCC": "Serine", + "UCA": "Serine", + "UCG": "Serine", + "UAU": "Tyrosine", + "UAC": "Tyrosine", + "UGU": "Cysteine", + "UGC": "Cysteine", + "UGG": "Tryptophan", + "UAA": "STOP", + "UAG": "STOP", + "UGA": "STOP", +} def of_codon(codon): if codon not in CODONS: - raise ValueError(f'Invalid codon: {codon}') + raise ValueError(f"Invalid codon: {codon}") return CODONS[codon] def proteins(strand): protein_list = [] for codon in map(of_codon, _chunkstring(strand, 3)): - if codon == 'STOP': + if codon == "STOP": break protein_list.append(codon) return protein_list def _chunkstring(string, number): - return (string[idx:number + idx] for idx in range(0, len(string), number)) + return (string[idx : number + idx] for idx in range(0, len(string), number)) diff --git a/evaluator/datasets/polyglot_py/proverb/solution.py b/evaluator/datasets/polyglot_py/proverb/solution.py index 8e6f30f55..b03281309 100644 --- a/evaluator/datasets/polyglot_py/proverb/solution.py +++ b/evaluator/datasets/polyglot_py/proverb/solution.py @@ -2,10 +2,12 @@ def proverb(*rhyme_items, qualifier): print(rhyme_items) if not rhyme_items: return [] - phrases = [f'For want of a {element_1} the {element_2} was lost.' - for element_1, element_2 in zip(rhyme_items, rhyme_items[1:])] + phrases = [ + f"For want of a {element_1} the {element_2} was lost." + for element_1, element_2 in zip(rhyme_items, rhyme_items[1:]) + ] if qualifier: - phrases.append(f'And all for the want of a {qualifier} {rhyme_items[0]}.') + phrases.append(f"And all for the want of a {qualifier} {rhyme_items[0]}.") else: - phrases.append(f'And all for the want of a {rhyme_items[0]}.') + phrases.append(f"And all for the want of a {rhyme_items[0]}.") return phrases diff --git a/evaluator/datasets/polyglot_py/proverb/tests.py b/evaluator/datasets/polyglot_py/proverb/tests.py index 7d1ebbb44..8ac7f7659 100644 --- a/evaluator/datasets/polyglot_py/proverb/tests.py +++ b/evaluator/datasets/polyglot_py/proverb/tests.py @@ -20,9 +20,7 @@ def test_zero_pieces(self): def test_one_piece(self): input_data = ["nail"] - self.assertEqual( - proverb(*input_data, qualifier=None), ["And all for the want of a nail."] - ) + self.assertEqual(proverb(*input_data, qualifier=None), ["And all for the want of a nail."]) def test_two_pieces(self): input_data = ["nail", "shoe"] diff --git a/evaluator/datasets/polyglot_py/pythagorean-triplet/main.py b/evaluator/datasets/polyglot_py/pythagorean-triplet/main.py index 196db6ee2..738300392 100644 --- a/evaluator/datasets/polyglot_py/pythagorean-triplet/main.py +++ b/evaluator/datasets/polyglot_py/pythagorean-triplet/main.py @@ -1,4 +1,5 @@ from typing import List + def triplets_with_sum(number: int) -> List[List[int]]: pass diff --git a/evaluator/datasets/polyglot_py/pythagorean-triplet/solution.py b/evaluator/datasets/polyglot_py/pythagorean-triplet/solution.py index 40b91444b..50dcaa8bf 100644 --- a/evaluator/datasets/polyglot_py/pythagorean-triplet/solution.py +++ b/evaluator/datasets/polyglot_py/pythagorean-triplet/solution.py @@ -1,4 +1,4 @@ -from math import sqrt, ceil, gcd +from math import ceil, gcd, sqrt def triplets_in_range(start, end): @@ -36,8 +36,8 @@ def primitive_triplets(limit): for more information """ for member_1, member_2 in euclidian_coprimes(limit): - calc_1 = member_1 ** 2 - calc_2 = member_2 ** 2 + calc_1 = member_1**2 + calc_2 = member_2**2 alpha = calc_1 - calc_2 beta = 2 * member_1 * member_2 @@ -50,8 +50,4 @@ def primitive_triplets(limit): def triplets_with_sum(number): - return [ - triplet for triplet - in triplets_in_range(1, number // 2) - if sum(triplet) == number - ] + return [triplet for triplet in triplets_in_range(1, number // 2) if sum(triplet) == number] diff --git a/evaluator/datasets/polyglot_py/queen-attack/main.py b/evaluator/datasets/polyglot_py/queen-attack/main.py index dbb498ca3..e7029c4aa 100644 --- a/evaluator/datasets/polyglot_py/queen-attack/main.py +++ b/evaluator/datasets/polyglot_py/queen-attack/main.py @@ -2,5 +2,5 @@ class Queen: def __init__(self, row: int, column: int) -> None: pass - def can_attack(self, another_queen: 'Queen') -> bool: + def can_attack(self, another_queen: "Queen") -> bool: pass diff --git a/evaluator/datasets/polyglot_py/queen-attack/solution.py b/evaluator/datasets/polyglot_py/queen-attack/solution.py index e9ae242ff..0edcb202f 100644 --- a/evaluator/datasets/polyglot_py/queen-attack/solution.py +++ b/evaluator/datasets/polyglot_py/queen-attack/solution.py @@ -1,13 +1,13 @@ class Queen: def __init__(self, row, column): if row < 0: - raise ValueError('row not positive') + raise ValueError("row not positive") if not 0 <= row <= 7: - raise ValueError('row not on board') + raise ValueError("row not on board") if column < 0: - raise ValueError('column not positive') + raise ValueError("column not positive") if not 0 <= column <= 7: - raise ValueError('column not on board') + raise ValueError("column not on board") self.row = row self.column = column @@ -15,7 +15,7 @@ def can_attack(self, another_queen): idx = abs(self.row - another_queen.row) edx = abs(self.column - another_queen.column) if idx == edx == 0: - raise ValueError('Invalid queen position: both queens in the same square') + raise ValueError("Invalid queen position: both queens in the same square") elif idx == edx or idx == 0 or edx == 0: return True else: diff --git a/evaluator/datasets/polyglot_py/rail-fence-cipher/solution.py b/evaluator/datasets/polyglot_py/rail-fence-cipher/solution.py index a27bd2670..307d01853 100644 --- a/evaluator/datasets/polyglot_py/rail-fence-cipher/solution.py +++ b/evaluator/datasets/polyglot_py/rail-fence-cipher/solution.py @@ -1,4 +1,4 @@ -from itertools import cycle, chain +from itertools import chain, cycle def fence_pattern(rails, size): @@ -8,10 +8,10 @@ def fence_pattern(rails, size): def encode(msg, rails): fence = fence_pattern(rails, len(msg)) - return ''.join(msg[idx] for _, idx in sorted(fence)) + return "".join(msg[idx] for _, idx in sorted(fence)) def decode(msg, rails): fence = fence_pattern(rails, len(msg)) fence_msg = zip(msg, sorted(fence)) - return ''.join(char for char, _ in sorted(fence_msg, key=lambda item: item[1][1])) + return "".join(char for char, _ in sorted(fence_msg, key=lambda item: item[1][1])) diff --git a/evaluator/datasets/polyglot_py/rail-fence-cipher/tests.py b/evaluator/datasets/polyglot_py/rail-fence-cipher/tests.py index 139752fd9..09285342c 100644 --- a/evaluator/datasets/polyglot_py/rail-fence-cipher/tests.py +++ b/evaluator/datasets/polyglot_py/rail-fence-cipher/tests.py @@ -15,17 +15,13 @@ def test_encode_with_two_rails(self): self.assertMultiLineEqual(encode("XOXOXOXOXOXOXOXOXO", 2), "XXXXXXXXXOOOOOOOOO") def test_encode_with_three_rails(self): - self.assertMultiLineEqual( - encode("WEAREDISCOVEREDFLEEATONCE", 3), "WECRLTEERDSOEEFEAOCAIVDEN" - ) + self.assertMultiLineEqual(encode("WEAREDISCOVEREDFLEEATONCE", 3), "WECRLTEERDSOEEFEAOCAIVDEN") def test_encode_with_ending_in_the_middle(self): self.assertMultiLineEqual(encode("EXERCISES", 4), "ESXIEECSR") def test_decode_with_three_rails(self): - self.assertMultiLineEqual( - decode("TEITELHDVLSNHDTISEIIEA", 3), "THEDEVILISINTHEDETAILS" - ) + self.assertMultiLineEqual(decode("TEITELHDVLSNHDTISEIIEA", 3), "THEDEVILISINTHEDETAILS") def test_decode_with_five_rails(self): self.assertMultiLineEqual(decode("EIEXMSMESAORIWSCE", 5), "EXERCISMISAWESOME") diff --git a/evaluator/datasets/polyglot_py/raindrops/solution.py b/evaluator/datasets/polyglot_py/raindrops/solution.py index 97eeadc33..4160ced6e 100644 --- a/evaluator/datasets/polyglot_py/raindrops/solution.py +++ b/evaluator/datasets/polyglot_py/raindrops/solution.py @@ -3,13 +3,13 @@ def convert(number): Converts a number to a string according to the raindrop sounds. """ - result = '' + result = "" if number % 3 == 0: - result += 'Pling' + result += "Pling" if number % 5 == 0: - result += 'Plang' + result += "Plang" if number % 7 == 0: - result += 'Plong' + result += "Plong" if not result: result = str(number) diff --git a/evaluator/datasets/polyglot_py/rational-numbers/main.py b/evaluator/datasets/polyglot_py/rational-numbers/main.py index fed73bfb6..0ba7f7b91 100644 --- a/evaluator/datasets/polyglot_py/rational-numbers/main.py +++ b/evaluator/datasets/polyglot_py/rational-numbers/main.py @@ -7,24 +7,24 @@ def __eq__(self, other: object) -> bool: return self.numer == other.numer and self.denom == other.denom def __repr__(self) -> str: - return f'{self.numer}/{self.denom}' + return f"{self.numer}/{self.denom}" - def __add__(self, other: 'Rational') -> 'Rational': + def __add__(self, other: "Rational") -> "Rational": pass - def __sub__(self, other: 'Rational') -> 'Rational': + def __sub__(self, other: "Rational") -> "Rational": pass - def __mul__(self, other: 'Rational') -> 'Rational': + def __mul__(self, other: "Rational") -> "Rational": pass - def __truediv__(self, other: 'Rational') -> 'Rational': + def __truediv__(self, other: "Rational") -> "Rational": pass - def __abs__(self) -> 'Rational': + def __abs__(self) -> "Rational": pass - def __pow__(self, power: int) -> 'Rational': + def __pow__(self, power: int) -> "Rational": pass def __rpow__(self, base: float) -> float: diff --git a/evaluator/datasets/polyglot_py/rational-numbers/solution.py b/evaluator/datasets/polyglot_py/rational-numbers/solution.py index d8a0b87be..df44b540f 100644 --- a/evaluator/datasets/polyglot_py/rational-numbers/solution.py +++ b/evaluator/datasets/polyglot_py/rational-numbers/solution.py @@ -17,7 +17,7 @@ def __eq__(self, other): return self.numer == other.numer and self.denom == other.denom def __repr__(self): - return f'{self.numer}/{self.denom}' + return f"{self.numer}/{self.denom}" def __add__(self, other): numer = (self.numer * other.denom) + (other.numer * self.denom) diff --git a/evaluator/datasets/polyglot_py/rational-numbers/tests.py b/evaluator/datasets/polyglot_py/rational-numbers/tests.py index 91c11073e..3e0c94260 100644 --- a/evaluator/datasets/polyglot_py/rational-numbers/tests.py +++ b/evaluator/datasets/polyglot_py/rational-numbers/tests.py @@ -10,7 +10,6 @@ class RationalNumbersTest(unittest.TestCase): - # Tests of type: Arithmetic # Addition diff --git a/evaluator/datasets/polyglot_py/react/main.py b/evaluator/datasets/polyglot_py/react/main.py index 28df45c69..672e50da1 100644 --- a/evaluator/datasets/polyglot_py/react/main.py +++ b/evaluator/datasets/polyglot_py/react/main.py @@ -15,4 +15,3 @@ def add_callback(self, callback: Callable) -> None: def remove_callback(self, callback: Callable) -> None: pass - \ No newline at end of file diff --git a/evaluator/datasets/polyglot_py/react/tests.py b/evaluator/datasets/polyglot_py/react/tests.py index 70cc849e8..24d538f56 100644 --- a/evaluator/datasets/polyglot_py/react/tests.py +++ b/evaluator/datasets/polyglot_py/react/tests.py @@ -2,12 +2,12 @@ # https://github.com/exercism/problem-specifications/tree/main/exercises/react/canonical-data.json # File last updated on 2023-07-19 -from functools import partial import unittest +from functools import partial from main import ( - InputCell, ComputeCell, + InputCell, ) diff --git a/evaluator/datasets/polyglot_py/rectangles/main.py b/evaluator/datasets/polyglot_py/rectangles/main.py index 79c68b17a..45ecbf549 100644 --- a/evaluator/datasets/polyglot_py/rectangles/main.py +++ b/evaluator/datasets/polyglot_py/rectangles/main.py @@ -1,4 +1,5 @@ from typing import List + def rectangles(strings: List[str]) -> int: pass diff --git a/evaluator/datasets/polyglot_py/rectangles/solution.py b/evaluator/datasets/polyglot_py/rectangles/solution.py index fe665729d..ae0df4fa2 100644 --- a/evaluator/datasets/polyglot_py/rectangles/solution.py +++ b/evaluator/datasets/polyglot_py/rectangles/solution.py @@ -8,7 +8,7 @@ def __init__(self, idx, jdx): self.jdx = jdx def __str__(self): - return '[' + str(self.idx) + ', ' + str(self.jdx) + ']' + return "[" + str(self.idx) + ", " + str(self.jdx) + "]" # return corner on the same line @@ -29,9 +29,12 @@ def same_col(index, list_obj): def search_corners(list_obj): - return [Corners(item, element) for item in range(len(list_obj)) - for element in range(len(list_obj[item])) - if list_obj[item][element] == '+'] + return [ + Corners(item, element) + for item in range(len(list_obj)) + for element in range(len(list_obj[item])) + if list_obj[item][element] == "+" + ] # validate that 4 points form a rectangle by @@ -58,16 +61,14 @@ def possible_rect(quartet): # validate path between two corners def path(corner1, corner2, item): if corner1.idx == corner2.idx: - for jdx in range(min(corner1.jdx + 1, corner2.jdx + 1), - max(corner1.jdx, corner2.jdx)): - if item[corner1.idx][jdx] != '-' and item[corner1.idx][jdx] != '+': + for jdx in range(min(corner1.jdx + 1, corner2.jdx + 1), max(corner1.jdx, corner2.jdx)): + if item[corner1.idx][jdx] != "-" and item[corner1.idx][jdx] != "+": return False return True elif corner1.jdx == corner2.jdx: - for idx in range(min(corner1.idx + 1, corner2.idx + 1), - max(corner1.idx, corner2.idx)): - if item[idx][corner1.jdx] != '|' and item[idx][corner1.jdx] != '+': + for idx in range(min(corner1.idx + 1, corner2.idx + 1), max(corner1.idx, corner2.idx)): + if item[idx][corner1.jdx] != "|" and item[idx][corner1.jdx] != "+": return False return True return None @@ -78,8 +79,8 @@ def validate_rect(rectangle, item): # validate connection at every corner # with neighbours on the same line and col for idx, _ in enumerate(rectangle): - line = same_line(rectangle[idx].idx, rectangle[0:idx] + rectangle[idx + 1:]) - column = same_col(rectangle[idx].jdx, rectangle[0:idx] + rectangle[idx + 1:]) + line = same_line(rectangle[idx].idx, rectangle[0:idx] + rectangle[idx + 1 :]) + column = same_col(rectangle[idx].jdx, rectangle[0:idx] + rectangle[idx + 1 :]) if not path(rectangle[idx], line, item) or not path(rectangle[idx], column, item): return False @@ -88,7 +89,7 @@ def validate_rect(rectangle, item): # count number of rectangles inside ASCII in input lines -def rectangles(strings=''): +def rectangles(strings=""): rectangle_total = 0 # test empty str if not strings: diff --git a/evaluator/datasets/polyglot_py/resistor-color-duo/main.py b/evaluator/datasets/polyglot_py/resistor-color-duo/main.py index 4f65d6986..b4500afaf 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-duo/main.py +++ b/evaluator/datasets/polyglot_py/resistor-color-duo/main.py @@ -1,4 +1,5 @@ from typing import List + def value(colors: List[str]) -> int: pass diff --git a/evaluator/datasets/polyglot_py/resistor-color-duo/solution.py b/evaluator/datasets/polyglot_py/resistor-color-duo/solution.py index 0f1285939..f7ad05d98 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-duo/solution.py +++ b/evaluator/datasets/polyglot_py/resistor-color-duo/solution.py @@ -1,15 +1,4 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] def value(colors): diff --git a/evaluator/datasets/polyglot_py/resistor-color-expert/main.py b/evaluator/datasets/polyglot_py/resistor-color-expert/main.py index 99708cc2a..1ba015851 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-expert/main.py +++ b/evaluator/datasets/polyglot_py/resistor-color-expert/main.py @@ -1,4 +1,5 @@ from typing import List + def resistor_label(colors: List[str]) -> str: pass diff --git a/evaluator/datasets/polyglot_py/resistor-color-expert/solution.py b/evaluator/datasets/polyglot_py/resistor-color-expert/solution.py index 0fd6e42fc..d447314c0 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-expert/solution.py +++ b/evaluator/datasets/polyglot_py/resistor-color-expert/solution.py @@ -1,49 +1,38 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] COLORS_TOLERANCE = { - 'brown': 1, - 'red': 2, - 'green': 0.5, - 'blue': 0.25, - 'violet': 0.1, - 'grey': 0.05, - 'gold': 5, - 'silver': 10 + "brown": 1, + "red": 2, + "green": 0.5, + "blue": 0.25, + "violet": 0.1, + "grey": 0.05, + "gold": 5, + "silver": 10, } def resistor_label(colors): if len(colors) == 1: - return f'0 ohms' + return "0 ohms" elif len(colors) == 4: value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1]) value *= 10 ** COLORS.index(colors[2]) value, unit = color_code(value) value = int(value) if value.is_integer() else value - return f'{value} {unit} ±{COLORS_TOLERANCE[colors[3]]}%' + return f"{value} {unit} ±{COLORS_TOLERANCE[colors[3]]}%" else: value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2]) value *= 10 ** COLORS.index(colors[3]) value, unit = color_code(value) value = int(value) if value.is_integer() else value - return f'{value} {unit} ±{COLORS_TOLERANCE[colors[4]]}%' + return f"{value} {unit} ±{COLORS_TOLERANCE[colors[4]]}%" def color_code(color): if color < 1000: - return color / 1, 'ohms' + return color / 1, "ohms" elif color < 1000000: - return color / 1000, 'kiloohms' + return color / 1000, "kiloohms" else: - return color / 1000000, 'megaohms' \ No newline at end of file + return color / 1000000, "megaohms" diff --git a/evaluator/datasets/polyglot_py/resistor-color-expert/tests.py b/evaluator/datasets/polyglot_py/resistor-color-expert/tests.py index a46a70a76..9f97d0692 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-expert/tests.py +++ b/evaluator/datasets/polyglot_py/resistor-color-expert/tests.py @@ -18,44 +18,28 @@ def test_red_black_red_and_green(self): self.assertEqual(resistor_label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%") def test_green_brown_orange_and_grey(self): - self.assertEqual( - resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%" - ) + self.assertEqual(resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%") def test_one_black_band(self): self.assertEqual(resistor_label(["black"]), "0 ohms") def test_orange_orange_yellow_black_and_brown(self): - self.assertEqual( - resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%" - ) + self.assertEqual(resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%") def test_red_green_yellow_yellow_and_brown(self): - self.assertEqual( - resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%" - ) + self.assertEqual(resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%") def test_blue_grey_white_brown_and_brown(self): - self.assertEqual( - resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%" - ) + self.assertEqual(resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%") def test_violet_orange_red_and_grey(self): - self.assertEqual( - resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%" - ) + self.assertEqual(resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%") def test_brown_red_orange_green_and_blue(self): - self.assertEqual( - resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%" - ) + self.assertEqual(resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%") def test_brown_black_brown_yellow_and_violet(self): - self.assertEqual( - resistor_label(["brown", "black", "brown", "yellow", "violet"]), "1.01 megaohms ±0.1%" - ) + self.assertEqual(resistor_label(["brown", "black", "brown", "yellow", "violet"]), "1.01 megaohms ±0.1%") def test_brown_black_red_and_red(self): - self.assertEqual( - resistor_label(["brown", "black", "red", "red"]), "1 kiloohms ±2%" - ) + self.assertEqual(resistor_label(["brown", "black", "red", "red"]), "1 kiloohms ±2%") diff --git a/evaluator/datasets/polyglot_py/resistor-color-trio/main.py b/evaluator/datasets/polyglot_py/resistor-color-trio/main.py index 39373da4c..1b252633b 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-trio/main.py +++ b/evaluator/datasets/polyglot_py/resistor-color-trio/main.py @@ -1,4 +1,5 @@ from typing import List + def label(colors: List[str]) -> str: pass diff --git a/evaluator/datasets/polyglot_py/resistor-color-trio/solution.py b/evaluator/datasets/polyglot_py/resistor-color-trio/solution.py index 69554592d..d07a70160 100644 --- a/evaluator/datasets/polyglot_py/resistor-color-trio/solution.py +++ b/evaluator/datasets/polyglot_py/resistor-color-trio/solution.py @@ -1,15 +1,4 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] def label(colors): @@ -17,16 +6,16 @@ def label(colors): value *= 10 ** COLORS.index(colors[2]) label = str(value) - if len(label) < 4 : - unit = 'ohms' + if len(label) < 4: + unit = "ohms" elif len(label) < 7: - label = str(value//1000) - unit = 'kiloohms' - elif len(label) <= 8 : - label = str(value//1000000) - unit = 'megaohms' + label = str(value // 1000) + unit = "kiloohms" + elif len(label) <= 8: + label = str(value // 1000000) + unit = "megaohms" elif len(label) >= 9: - label = str(value//1000000000) - unit = 'gigaohms' + label = str(value // 1000000000) + unit = "gigaohms" - return f'{value if value < 1000 else label} {unit}' + return f"{value if value < 1000 else label} {unit}" diff --git a/evaluator/datasets/polyglot_py/resistor-color/main.py b/evaluator/datasets/polyglot_py/resistor-color/main.py index a583eaa9c..0084e05fd 100644 --- a/evaluator/datasets/polyglot_py/resistor-color/main.py +++ b/evaluator/datasets/polyglot_py/resistor-color/main.py @@ -1,5 +1,6 @@ from typing import List + def color_code(color: str) -> int: pass diff --git a/evaluator/datasets/polyglot_py/resistor-color/solution.py b/evaluator/datasets/polyglot_py/resistor-color/solution.py index 83c67f4f1..12b918ccb 100644 --- a/evaluator/datasets/polyglot_py/resistor-color/solution.py +++ b/evaluator/datasets/polyglot_py/resistor-color/solution.py @@ -1,15 +1,4 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] def color_code(color): diff --git a/evaluator/datasets/polyglot_py/rest-api/solution.py b/evaluator/datasets/polyglot_py/rest-api/solution.py index 46556efce..7979fde61 100644 --- a/evaluator/datasets/polyglot_py/rest-api/solution.py +++ b/evaluator/datasets/polyglot_py/rest-api/solution.py @@ -3,12 +3,12 @@ class RestAPI: def __init__(self, database=None): - self.database = database or {'users': []} + self.database = database or {"users": []} def update(self): - for user in self.database['users']: - owed_by = user['owed_by'] - owes = user['owes'] + for user in self.database["users"]: + owed_by = user["owed_by"] + owes = user["owes"] for debtor in list(owed_by.keys()): if debtor in owes: diff = 0 @@ -22,65 +22,54 @@ def update(self): owes[debtor] = diff elif diff < 0: owed_by[debtor] = -diff - user['balance'] = sum(owed_by.values()) - sum(owes.values()) + user["balance"] = sum(owed_by.values()) - sum(owes.values()) def get(self, url, payload=None): if payload is not None: payload = json.loads(payload) - if url == '/users': + if url == "/users": if payload is None: return json.dumps(self.database) else: - return json.dumps({ - 'users': [ - user for user in self.database['users'] - if user['name'] in payload['users'] - ] - }) + return json.dumps( + {"users": [user for user in self.database["users"] if user["name"] in payload["users"]]} + ) return None def post(self, url, payload=None): result = None if payload is not None: payload = json.loads(payload) - if url == '/add': + if url == "/add": if payload is not None: - name = payload['user'] - users = self.database['users'] + name = payload["user"] + users = self.database["users"] user = None for idx in users: - if idx['name'] == name: + if idx["name"] == name: user = idx break if user is None: - new_user = { - 'name': name, - 'owes': {}, - 'owed_by': {}, - 'balance': 0 - } + new_user = {"name": name, "owes": {}, "owed_by": {}, "balance": 0} users.append(new_user) self.update() result = json.dumps(new_user) - elif url == '/iou': + elif url == "/iou": if payload is not None: - lender_name = payload['lender'] - borrower_name = payload['borrower'] - amount = payload['amount'] + lender_name = payload["lender"] + borrower_name = payload["borrower"] + amount = payload["amount"] lender = borrower = None - for user in self.database['users']: - if user['name'] == lender_name: + for user in self.database["users"]: + if user["name"] == lender_name: lender = user - elif user['name'] == borrower_name: + elif user["name"] == borrower_name: borrower = user if lender is not None and borrower is not None: - lender['owed_by'].setdefault(borrower_name, 0) - lender['owed_by'][borrower_name] += amount - borrower['owes'].setdefault(lender_name, 0) - borrower['owes'][lender_name] += amount + lender["owed_by"].setdefault(borrower_name, 0) + lender["owed_by"][borrower_name] += amount + borrower["owes"].setdefault(lender_name, 0) + borrower["owes"][lender_name] += amount self.update() - result = self.get( - '/users', - json.dumps({'users': [lender_name, borrower_name]}) - ) + result = self.get("/users", json.dumps({"users": [lender_name, borrower_name]})) return result diff --git a/evaluator/datasets/polyglot_py/rest-api/tests.py b/evaluator/datasets/polyglot_py/rest-api/tests.py index aa334bca0..3b5f95b89 100644 --- a/evaluator/datasets/polyglot_py/rest-api/tests.py +++ b/evaluator/datasets/polyglot_py/rest-api/tests.py @@ -37,9 +37,7 @@ def test_get_single_user(self): api = RestAPI(database) payload = json.dumps({"users": ["Bob"]}) response = api.get("/users", payload) - expected = { - "users": [{"name": "Bob", "owes": {}, "owed_by": {}, "balance": 0.0}] - } + expected = {"users": [{"name": "Bob", "owes": {}, "owed_by": {}, "balance": 0.0}]} self.assertDictEqual(json.loads(response), expected) def test_both_users_have_0_balance(self): diff --git a/evaluator/datasets/polyglot_py/reverse-string/solution.py b/evaluator/datasets/polyglot_py/reverse-string/solution.py index c8b38d579..3d6b8fb8c 100644 --- a/evaluator/datasets/polyglot_py/reverse-string/solution.py +++ b/evaluator/datasets/polyglot_py/reverse-string/solution.py @@ -1,2 +1,2 @@ -def reverse(text=''): +def reverse(text=""): return text[::-1] diff --git a/evaluator/datasets/polyglot_py/rna-transcription/solution.py b/evaluator/datasets/polyglot_py/rna-transcription/solution.py index 61db942ea..ed94e045c 100644 --- a/evaluator/datasets/polyglot_py/rna-transcription/solution.py +++ b/evaluator/datasets/polyglot_py/rna-transcription/solution.py @@ -1,4 +1,5 @@ DNA_TO_RNA = str.maketrans("AGCT", "UCGA") + def to_rna(dna_strand): return dna_strand.translate(DNA_TO_RNA) diff --git a/evaluator/datasets/polyglot_py/robot-name/solution.py b/evaluator/datasets/polyglot_py/robot-name/solution.py index eb27b48f3..1dd57ed17 100644 --- a/evaluator/datasets/polyglot_py/robot-name/solution.py +++ b/evaluator/datasets/polyglot_py/robot-name/solution.py @@ -1,6 +1,7 @@ import random -ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + class Robot: _used_names = set() # Class variable to track all names globally @@ -9,16 +10,10 @@ def __init__(self): self._name = None def prefix(self): - return ''.join([ - random.choice(ALPHABET) - for _ in range(0, 2) - ]) + return "".join([random.choice(ALPHABET) for _ in range(0, 2)]) def suffix(self): - return ''.join([ - str(random.choice(range(0, 10))) - for _ in range(0, 3) - ]) + return "".join([str(random.choice(range(0, 10))) for _ in range(0, 3)]) def get_name(self): if not self._name: diff --git a/evaluator/datasets/polyglot_py/robot-name/tests.py b/evaluator/datasets/polyglot_py/robot-name/tests.py index 2cc8e2bf1..5fe5ece27 100644 --- a/evaluator/datasets/polyglot_py/robot-name/tests.py +++ b/evaluator/datasets/polyglot_py/robot-name/tests.py @@ -1,5 +1,5 @@ -import unittest import random +import unittest from main import Robot @@ -10,7 +10,7 @@ class RobotNameTest(unittest.TestCase): if not hasattr(unittest.TestCase, "assertRegex"): assertRegex = unittest.TestCase.assertRegexpMatches - name_re = r'^[A-Z]{2}\d{3}$' + name_re = r"^[A-Z]{2}\d{3}$" def test_has_name(self): self.assertRegex(Robot().name, self.name_re) @@ -21,10 +21,7 @@ def test_name_sticks(self): self.assertEqual(robot.name, robot.name) def test_different_robots_have_different_names(self): - self.assertNotEqual( - Robot().name, - Robot().name - ) + self.assertNotEqual(Robot().name, Robot().name) def test_reset_name(self): # Set a seed @@ -47,5 +44,5 @@ def test_reset_name(self): self.assertRegex(name2, self.name_re) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/robot-simulator/main.py b/evaluator/datasets/polyglot_py/robot-simulator/main.py index 72caa3b59..315016ff7 100644 --- a/evaluator/datasets/polyglot_py/robot-simulator/main.py +++ b/evaluator/datasets/polyglot_py/robot-simulator/main.py @@ -1,5 +1,3 @@ -from typing import Tuple - # Globals for the directions # Change the values as you see fit EAST = None diff --git a/evaluator/datasets/polyglot_py/robot-simulator/solution.py b/evaluator/datasets/polyglot_py/robot-simulator/solution.py index 20caf3bed..f34748f07 100644 --- a/evaluator/datasets/polyglot_py/robot-simulator/solution.py +++ b/evaluator/datasets/polyglot_py/robot-simulator/solution.py @@ -37,9 +37,7 @@ def turn_right(self): self.compass.right() def move(self, commands): - instructions = {'A': self.advance, - 'R': self.turn_right, - 'L': self.turn_left} + instructions = {"A": self.advance, "R": self.turn_right, "L": self.turn_left} for cmd in commands: if cmd in instructions: instructions[cmd]() diff --git a/evaluator/datasets/polyglot_py/robot-simulator/tests.py b/evaluator/datasets/polyglot_py/robot-simulator/tests.py index b09f45a3f..0c283f68f 100644 --- a/evaluator/datasets/polyglot_py/robot-simulator/tests.py +++ b/evaluator/datasets/polyglot_py/robot-simulator/tests.py @@ -5,16 +5,15 @@ import unittest from main import ( - Robot, - NORTH, EAST, + NORTH, SOUTH, WEST, + Robot, ) class RobotSimulatorTest(unittest.TestCase): - # Test create robot def test_at_origin_facing_north(self): robot = Robot(NORTH, 0, 0) diff --git a/evaluator/datasets/polyglot_py/roman-numerals/main.py b/evaluator/datasets/polyglot_py/roman-numerals/main.py index a6bbd999d..27c4f859e 100644 --- a/evaluator/datasets/polyglot_py/roman-numerals/main.py +++ b/evaluator/datasets/polyglot_py/roman-numerals/main.py @@ -1,3 +1,2 @@ def roman(number: int) -> str: pass - diff --git a/evaluator/datasets/polyglot_py/roman-numerals/solution.py b/evaluator/datasets/polyglot_py/roman-numerals/solution.py index c6e56ec03..a9fbd19e6 100644 --- a/evaluator/datasets/polyglot_py/roman-numerals/solution.py +++ b/evaluator/datasets/polyglot_py/roman-numerals/solution.py @@ -1,16 +1,22 @@ NUMERAL_MAPPINGS = ( - (1000, 'M'), (900, 'CM'), - (500, 'D'), (400, 'CD'), - (100, 'C'), (90, 'XC'), - (50, 'L'), (40, 'XL'), - (10, 'X'), (9, 'IX'), - (5, 'V'), (4, 'IV'), - (1, 'I') + (1000, "M"), + (900, "CM"), + (500, "D"), + (400, "CD"), + (100, "C"), + (90, "XC"), + (50, "L"), + (40, "XL"), + (10, "X"), + (9, "IX"), + (5, "V"), + (4, "IV"), + (1, "I"), ) def roman(number): - result = '' + result = "" for arabic_num, roman_num in NUMERAL_MAPPINGS: while number >= arabic_num: result += roman_num diff --git a/evaluator/datasets/polyglot_py/rotational-cipher/solution.py b/evaluator/datasets/polyglot_py/rotational-cipher/solution.py index 332632754..6fb35dcf3 100644 --- a/evaluator/datasets/polyglot_py/rotational-cipher/solution.py +++ b/evaluator/datasets/polyglot_py/rotational-cipher/solution.py @@ -1,11 +1,10 @@ from string import ascii_lowercase, ascii_uppercase - ALPHA_LEN = len(ascii_lowercase) def rotate(message, key): - coded_message = '' + coded_message = "" for char in message: if char in ascii_lowercase: char = ascii_lowercase[(ascii_lowercase.index(char) + key) % ALPHA_LEN] diff --git a/evaluator/datasets/polyglot_py/run-length-encoding/solution.py b/evaluator/datasets/polyglot_py/run-length-encoding/solution.py index 309c8a3b1..33e60209c 100644 --- a/evaluator/datasets/polyglot_py/run-length-encoding/solution.py +++ b/evaluator/datasets/polyglot_py/run-length-encoding/solution.py @@ -3,11 +3,12 @@ def decode(string): - return sub(r'(\d+)(\D)', lambda main: main.group(2) * int(main.group(1)), string) + return sub(r"(\d+)(\D)", lambda main: main.group(2) * int(main.group(1)), string) def encode(string): def single_helper(key, group): size = len(list(group)) return key if size == 1 else str(size) + key - return ''.join(single_helper(key, group) for key, group in groupby(string)) + + return "".join(single_helper(key, group) for key, group in groupby(string)) diff --git a/evaluator/datasets/polyglot_py/run-length-encoding/tests.py b/evaluator/datasets/polyglot_py/run-length-encoding/tests.py index 042924eee..0217da5d5 100644 --- a/evaluator/datasets/polyglot_py/run-length-encoding/tests.py +++ b/evaluator/datasets/polyglot_py/run-length-encoding/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - encode, decode, + encode, ) diff --git a/evaluator/datasets/polyglot_py/saddle-points/solution.py b/evaluator/datasets/polyglot_py/saddle-points/solution.py index ff11e68a5..86a7cd370 100644 --- a/evaluator/datasets/polyglot_py/saddle-points/solution.py +++ b/evaluator/datasets/polyglot_py/saddle-points/solution.py @@ -3,14 +3,16 @@ def saddle_points(matrix): return [] if any(len(row) != len(matrix[0]) for row in matrix): - raise ValueError('irregular matrix') + raise ValueError("irregular matrix") mmax = [max(row) for row in matrix] mmin = [min(col) for col in zip(*matrix)] - points = [{'row': index + 1, 'column': col_index + 1} - for index, _ in enumerate(matrix) - for col_index, _ in enumerate(matrix[0]) - if mmax[index] == mmin[col_index]] + points = [ + {"row": index + 1, "column": col_index + 1} + for index, _ in enumerate(matrix) + for col_index, _ in enumerate(matrix[0]) + if mmax[index] == mmin[col_index] + ] return points or [] diff --git a/evaluator/datasets/polyglot_py/satellite/main.py b/evaluator/datasets/polyglot_py/satellite/main.py index 98c1bb51d..7cb00785b 100644 --- a/evaluator/datasets/polyglot_py/satellite/main.py +++ b/evaluator/datasets/polyglot_py/satellite/main.py @@ -1,4 +1,5 @@ -from typing import List, Dict +from typing import Dict, List + def tree_from_traversals(preorder: List[str], inorder: List[str]) -> Dict: pass diff --git a/evaluator/datasets/polyglot_py/satellite/solution.py b/evaluator/datasets/polyglot_py/satellite/solution.py index 310a89aee..fef73ef9a 100644 --- a/evaluator/datasets/polyglot_py/satellite/solution.py +++ b/evaluator/datasets/polyglot_py/satellite/solution.py @@ -1,16 +1,16 @@ def tree_from_traversals(preorder, inorder): if len(preorder) != len(inorder): - raise ValueError('traversals must have the same length') + raise ValueError("traversals must have the same length") if set(preorder) != set(inorder): - raise ValueError('traversals must have the same elements') + raise ValueError("traversals must have the same elements") if len(set(preorder)) != len(preorder) != len(set(inorder)): - raise ValueError('traversals must contain unique items') + raise ValueError("traversals must contain unique items") if not preorder: return {} value = preorder.pop(0) index = inorder.index(value) - left_inorder, right_inorder = inorder[:index], inorder[index+1:] + left_inorder, right_inorder = inorder[:index], inorder[index + 1 :] left_preorder = [idx for idx in preorder if idx in left_inorder] right_preorder = [idx for idx in preorder if idx in right_inorder] @@ -18,4 +18,4 @@ def tree_from_traversals(preorder, inorder): left = tree_from_traversals(left_preorder, left_inorder) right = tree_from_traversals(right_preorder, right_inorder) - return {'v': value, 'l': left, 'r': right} + return {"v": value, "l": left, "r": right} diff --git a/evaluator/datasets/polyglot_py/satellite/tests.py b/evaluator/datasets/polyglot_py/satellite/tests.py index 9bc8118c0..588fb4fdc 100644 --- a/evaluator/datasets/polyglot_py/satellite/tests.py +++ b/evaluator/datasets/polyglot_py/satellite/tests.py @@ -55,9 +55,7 @@ def test_reject_inconsistent_traversals_of_same_length(self): with self.assertRaises(ValueError) as err: tree_from_traversals(preorder, inorder) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "traversals must have the same elements" - ) + self.assertEqual(err.exception.args[0], "traversals must have the same elements") def test_reject_traversals_with_repeated_items(self): preorder = ["a", "b", "a"] diff --git a/evaluator/datasets/polyglot_py/say/solution.py b/evaluator/datasets/polyglot_py/say/solution.py index 95d6d4477..7a409f4e1 100644 --- a/evaluator/datasets/polyglot_py/say/solution.py +++ b/evaluator/datasets/polyglot_py/say/solution.py @@ -1,11 +1,41 @@ def say(number): - small = dict(enumerate(( - 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', - 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', - 'sixteen', 'seventeen', 'eighteen', 'nineteen'))) + small = dict( + enumerate( + ( + "zero", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "ten", + "eleven", + "twelve", + "thirteen", + "fourteen", + "fifteen", + "sixteen", + "seventeen", + "eighteen", + "nineteen", + ) + ) + ) - tens = {20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', - 60: 'sixty', 70: 'seventy', 80: 'eighty', 90: 'ninety'} + tens = { + 20: "twenty", + 30: "thirty", + 40: "forty", + 50: "fifty", + 60: "sixty", + 70: "seventy", + 80: "eighty", + 90: "ninety", + } kilo = 1e3 mega = 1e6 @@ -13,9 +43,9 @@ def say(number): tera = 1e12 if number < 0: - raise ValueError('input out of range') + raise ValueError("input out of range") if number >= tera: - raise ValueError('input out of range') + raise ValueError("input out of range") if number < 20: return small[number] @@ -23,23 +53,23 @@ def say(number): if number < 100: if number % 10 == 0: return tens[number] - return tens[number // 10 * 10] + '-' + small[number % 10] + return tens[number // 10 * 10] + "-" + small[number % 10] if number < kilo: if number % 100 == 0: - return small[number // 100] + ' hundred' - return small[number // 100] + ' hundred ' + say(number % 100) + return small[number // 100] + " hundred" + return small[number // 100] + " hundred " + say(number % 100) if number < mega: if number % kilo == 0: - return say(number // kilo) + ' thousand' - return say(number // kilo) + ' thousand ' + say(number % kilo) + return say(number // kilo) + " thousand" + return say(number // kilo) + " thousand " + say(number % kilo) if number < giga: if number % mega == 0: - return say(number // mega) + ' million' - return say(number // mega) + ' million ' + say(number % mega) + return say(number // mega) + " million" + return say(number // mega) + " million " + say(number % mega) if number % giga == 0: - return say(number // giga) + ' billion' - return say(number // giga) + ' billion ' + say(number % giga) \ No newline at end of file + return say(number // giga) + " billion" + return say(number // giga) + " billion " + say(number % giga) diff --git a/evaluator/datasets/polyglot_py/say/tests.py b/evaluator/datasets/polyglot_py/say/tests.py index 3548a0d79..3d27a96a7 100644 --- a/evaluator/datasets/polyglot_py/say/tests.py +++ b/evaluator/datasets/polyglot_py/say/tests.py @@ -53,9 +53,7 @@ def test_one_million(self): self.assertEqual(say(1000000), "one million") def test_one_million_two_thousand_three_hundred_forty_five(self): - self.assertEqual( - say(1002345), "one million two thousand three hundred forty-five" - ) + self.assertEqual(say(1002345), "one million two thousand three hundred forty-five") def test_one_billion(self): self.assertEqual(say(1000000000), "one billion") diff --git a/evaluator/datasets/polyglot_py/scale-generator/solution.py b/evaluator/datasets/polyglot_py/scale-generator/solution.py index 19c0c816c..22dd4a9e5 100644 --- a/evaluator/datasets/polyglot_py/scale-generator/solution.py +++ b/evaluator/datasets/polyglot_py/scale-generator/solution.py @@ -1,13 +1,13 @@ class Scale: - ASCENDING_INTERVALS = ['m', 'M', 'A'] - CHROMATIC_SCALE = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'] - FLAT_CHROMATIC_SCALE = ['A', 'Bb', 'B', 'C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab'] - FLAT_KEYS = ['F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb', 'd', 'g', 'c', 'f', 'bb', 'eb'] + ASCENDING_INTERVALS = ["m", "M", "A"] + CHROMATIC_SCALE = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"] + FLAT_CHROMATIC_SCALE = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"] + FLAT_KEYS = ["F", "Bb", "Eb", "Ab", "Db", "Gb", "d", "g", "c", "f", "bb", "eb"] def __init__(self, tonic, intervals=None): self.tonic = tonic.capitalize() self.intervals = intervals - self.chromatic_scale = (self.FLAT_CHROMATIC_SCALE if tonic in self.FLAT_KEYS else self.CHROMATIC_SCALE) + self.chromatic_scale = self.FLAT_CHROMATIC_SCALE if tonic in self.FLAT_KEYS else self.CHROMATIC_SCALE def chromatic(self): return self._reorder_chromatic_scale() @@ -27,4 +27,4 @@ def interval(self, intervals): def _reorder_chromatic_scale(self): index = self.chromatic_scale.index(self.tonic) - return self.chromatic_scale[index:] + self.chromatic_scale[:index] + return self.chromatic_scale[index:] + self.chromatic_scale[:index] diff --git a/evaluator/datasets/polyglot_py/scale-generator/tests.py b/evaluator/datasets/polyglot_py/scale-generator/tests.py index d12282aff..3e4028f7b 100644 --- a/evaluator/datasets/polyglot_py/scale-generator/tests.py +++ b/evaluator/datasets/polyglot_py/scale-generator/tests.py @@ -10,7 +10,6 @@ class ScaleGeneratorTest(unittest.TestCase): - # Test chromatic scales def test_chromatic_scale_with_sharps(self): expected = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] diff --git a/evaluator/datasets/polyglot_py/scrabble-score/solution.py b/evaluator/datasets/polyglot_py/scrabble-score/solution.py index 8ab7d58df..e78f18984 100644 --- a/evaluator/datasets/polyglot_py/scrabble-score/solution.py +++ b/evaluator/datasets/polyglot_py/scrabble-score/solution.py @@ -1,10 +1,30 @@ POINTS = { - 'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, - 'f': 4, 'g': 2, 'h': 4, 'i': 1, 'j': 8, - 'k': 5, 'l': 1, 'm': 3, 'n': 1, 'o': 1, - 'p': 3, 'q': 10, 'r': 1, 's': 1, 't': 1, - 'u': 1, 'v': 4, 'w': 4, 'x': 8, 'y': 4, - 'z': 10 + "a": 1, + "b": 3, + "c": 3, + "d": 2, + "e": 1, + "f": 4, + "g": 2, + "h": 4, + "i": 1, + "j": 8, + "k": 5, + "l": 1, + "m": 3, + "n": 1, + "o": 1, + "p": 3, + "q": 10, + "r": 1, + "s": 1, + "t": 1, + "u": 1, + "v": 4, + "w": 4, + "x": 8, + "y": 4, + "z": 10, } diff --git a/evaluator/datasets/polyglot_py/secret-handshake/main.py b/evaluator/datasets/polyglot_py/secret-handshake/main.py index 4f66963b4..a3b075de1 100644 --- a/evaluator/datasets/polyglot_py/secret-handshake/main.py +++ b/evaluator/datasets/polyglot_py/secret-handshake/main.py @@ -1,4 +1,5 @@ from typing import List + def commands(binary_str: str) -> List[str]: pass diff --git a/evaluator/datasets/polyglot_py/secret-handshake/solution.py b/evaluator/datasets/polyglot_py/secret-handshake/solution.py index 14e681d93..7728b5cfa 100644 --- a/evaluator/datasets/polyglot_py/secret-handshake/solution.py +++ b/evaluator/datasets/polyglot_py/secret-handshake/solution.py @@ -1,7 +1,7 @@ -GESTURES = ['jump', 'close your eyes', 'double blink', 'wink'] +GESTURES = ["jump", "close your eyes", "double blink", "wink"] def commands(binary_str): - reverse, *bits = [digit == '1' for digit in binary_str] + reverse, *bits = [digit == "1" for digit in binary_str] actions = [gesture for gesture, bit in zip(GESTURES, bits) if bit] return actions if reverse else actions[::-1] diff --git a/evaluator/datasets/polyglot_py/secret-handshake/tests.py b/evaluator/datasets/polyglot_py/secret-handshake/tests.py index 45af2106a..9e2078832 100644 --- a/evaluator/datasets/polyglot_py/secret-handshake/tests.py +++ b/evaluator/datasets/polyglot_py/secret-handshake/tests.py @@ -35,14 +35,10 @@ def test_reversing_no_actions_still_gives_no_actions(self): self.assertEqual(commands("10000"), []) def test_all_possible_actions(self): - self.assertEqual( - commands("01111"), ["wink", "double blink", "close your eyes", "jump"] - ) + self.assertEqual(commands("01111"), ["wink", "double blink", "close your eyes", "jump"]) def test_reverse_all_possible_actions(self): - self.assertEqual( - commands("11111"), ["jump", "close your eyes", "double blink", "wink"] - ) + self.assertEqual(commands("11111"), ["jump", "close your eyes", "double blink", "wink"]) def test_do_nothing_for_zero(self): self.assertEqual(commands("00000"), []) diff --git a/evaluator/datasets/polyglot_py/series/main.py b/evaluator/datasets/polyglot_py/series/main.py index bca440a18..b01e020e9 100644 --- a/evaluator/datasets/polyglot_py/series/main.py +++ b/evaluator/datasets/polyglot_py/series/main.py @@ -1,4 +1,5 @@ from typing import List + def slices(series: str, length: int) -> List[str]: pass diff --git a/evaluator/datasets/polyglot_py/series/solution.py b/evaluator/datasets/polyglot_py/series/solution.py index 447819fc5..04db528b0 100644 --- a/evaluator/datasets/polyglot_py/series/solution.py +++ b/evaluator/datasets/polyglot_py/series/solution.py @@ -1,11 +1,11 @@ def slices(series, length): if not series: - raise ValueError('series cannot be empty') + raise ValueError("series cannot be empty") elif length == 0: - raise ValueError('slice length cannot be zero') + raise ValueError("slice length cannot be zero") elif length < 0: - raise ValueError('slice length cannot be negative') + raise ValueError("slice length cannot be negative") elif len(series) < length: - raise ValueError('slice length cannot be greater than series length') + raise ValueError("slice length cannot be greater than series length") - return [series[idx:idx + length] for idx in range(len(series) - length + 1)] + return [series[idx : idx + length] for idx in range(len(series) - length + 1)] diff --git a/evaluator/datasets/polyglot_py/series/tests.py b/evaluator/datasets/polyglot_py/series/tests.py index 47f8b8e9c..e5d0ae082 100644 --- a/evaluator/datasets/polyglot_py/series/tests.py +++ b/evaluator/datasets/polyglot_py/series/tests.py @@ -35,17 +35,13 @@ def test_slice_length_is_too_large(self): with self.assertRaises(ValueError) as err: slices("12345", 6) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "slice length cannot be greater than series length" - ) + self.assertEqual(err.exception.args[0], "slice length cannot be greater than series length") def test_slice_length_is_way_too_large(self): with self.assertRaises(ValueError) as err: slices("12345", 42) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "slice length cannot be greater than series length" - ) + self.assertEqual(err.exception.args[0], "slice length cannot be greater than series length") def test_slice_length_cannot_be_zero(self): with self.assertRaises(ValueError) as err: diff --git a/evaluator/datasets/polyglot_py/sgf-parsing/solution.py b/evaluator/datasets/polyglot_py/sgf-parsing/solution.py index dcdc74c73..82341b9fc 100644 --- a/evaluator/datasets/polyglot_py/sgf-parsing/solution.py +++ b/evaluator/datasets/polyglot_py/sgf-parsing/solution.py @@ -1,4 +1,5 @@ """Parse an SGF tree.""" + import collections import string @@ -32,7 +33,7 @@ def parse_property_vals(sgf: str, idx: int) -> tuple[int, list[str]]: while sgf[idx] != "]": # \ has special SGF handling. if sgf[idx] == "\\": - if sgf[idx:idx + 2] == "\\\n": + if sgf[idx : idx + 2] == "\\\n": # Newlines are removed if they come immediately after a \, # otherwise they remain as newlines. pass @@ -75,7 +76,7 @@ def parse_node(sgf: str) -> SgfTree: raise ValueError("propery key is empty") prop_key = sgf[prop_key_start:idx] if not prop_key.isupper(): - raise ValueError('property must be in uppercase') + raise ValueError("property must be in uppercase") idx, prop_vals = parse_property_vals(sgf, idx) properties[prop_key].extend(prop_vals) @@ -106,15 +107,15 @@ def parse_node(sgf: str) -> SgfTree: idx += 1 if idx > prop_key_start and not properties: - raise ValueError('properties without delimiter') + raise ValueError("properties without delimiter") return SgfTree(children=children, properties=dict(properties)) def parse(sgf: str) -> SgfTree: """Parse an SGF tree.""" if not sgf.startswith("(") and not sgf.endswith(")"): - raise ValueError('tree missing') + raise ValueError("tree missing") if not sgf.startswith("(;"): - raise ValueError('tree with no nodes') + raise ValueError("tree with no nodes") inside = sgf[1:-1] return parse_node(inside) diff --git a/evaluator/datasets/polyglot_py/sgf-parsing/tests.py b/evaluator/datasets/polyglot_py/sgf-parsing/tests.py index f86cf3e5e..0a36b64b5 100644 --- a/evaluator/datasets/polyglot_py/sgf-parsing/tests.py +++ b/evaluator/datasets/polyglot_py/sgf-parsing/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - parse, SgfTree, + parse, ) diff --git a/evaluator/datasets/polyglot_py/sieve/solution.py b/evaluator/datasets/polyglot_py/sieve/solution.py index 0e8ae8ad3..e43d2c299 100644 --- a/evaluator/datasets/polyglot_py/sieve/solution.py +++ b/evaluator/datasets/polyglot_py/sieve/solution.py @@ -1,7 +1,7 @@ def primes(limit): prime = [True] * (limit + 1) prime[0] = prime[1] = False - for idx in range(2, int(limit ** 0.5) + 1): + for idx in range(2, int(limit**0.5) + 1): if prime[idx]: for edx in range(idx * idx, limit + 1, idx): prime[edx] = False diff --git a/evaluator/datasets/polyglot_py/simple-cipher/main.py b/evaluator/datasets/polyglot_py/simple-cipher/main.py index 41deb4407..290e1d8e8 100644 --- a/evaluator/datasets/polyglot_py/simple-cipher/main.py +++ b/evaluator/datasets/polyglot_py/simple-cipher/main.py @@ -1,5 +1,6 @@ from typing import Optional + class Cipher: def __init__(self, key: Optional[str] = None) -> None: pass diff --git a/evaluator/datasets/polyglot_py/simple-cipher/solution.py b/evaluator/datasets/polyglot_py/simple-cipher/solution.py index 61abf58d2..9f1d76fee 100644 --- a/evaluator/datasets/polyglot_py/simple-cipher/solution.py +++ b/evaluator/datasets/polyglot_py/simple-cipher/solution.py @@ -1,25 +1,23 @@ -from string import ascii_lowercase -from time import time import random from itertools import cycle +from string import ascii_lowercase +from time import time class Cipher: - def __init__(self, key=None): if key is None: random.seed(time()) - key = ''.join(random.choice(ascii_lowercase) for _ in range(100)) + key = "".join(random.choice(ascii_lowercase) for _ in range(100)) self.key = key def encode(self, text): - return ''.join( - chr(((ord(character) - 2 * ord('a') + ord(key)) % 26) + ord('a')) + return "".join( + chr(((ord(character) - 2 * ord("a") + ord(key)) % 26) + ord("a")) for character, key in zip(text, cycle(self.key)) ) def decode(self, text): - return ''.join( - chr(((ord(character) - ord(key) + 26) % 26) + ord('a')) - for character, key in zip(text, cycle(self.key)) + return "".join( + chr(((ord(character) - ord(key) + 26) % 26) + ord("a")) for character, key in zip(text, cycle(self.key)) ) diff --git a/evaluator/datasets/polyglot_py/simple-linked-list/solution.py b/evaluator/datasets/polyglot_py/simple-linked-list/solution.py index f87b155e7..41d3ccbf2 100644 --- a/evaluator/datasets/polyglot_py/simple-linked-list/solution.py +++ b/evaluator/datasets/polyglot_py/simple-linked-list/solution.py @@ -44,7 +44,7 @@ def __len__(self): def head(self): if self._head is None: - raise EmptyListException('The list is empty.') + raise EmptyListException("The list is empty.") return self._head def push(self, value): @@ -55,7 +55,7 @@ def push(self, value): def pop(self): if self._head is None: - raise EmptyListException('The list is empty.') + raise EmptyListException("The list is empty.") self._len -= 1 ret = self._head.value() self._head = self._head.next() diff --git a/evaluator/datasets/polyglot_py/simple-linked-list/tests.py b/evaluator/datasets/polyglot_py/simple-linked-list/tests.py index d999e2b26..7b7451a7f 100644 --- a/evaluator/datasets/polyglot_py/simple-linked-list/tests.py +++ b/evaluator/datasets/polyglot_py/simple-linked-list/tests.py @@ -1,10 +1,10 @@ import unittest -from main import LinkedList, EmptyListException - +from main import EmptyListException, LinkedList # No canonical data available for this exercise + class SimpleLinkedListTest(unittest.TestCase): def test_empty_list_has_len_zero(self): sut = LinkedList() diff --git a/evaluator/datasets/polyglot_py/space-age/solution.py b/evaluator/datasets/polyglot_py/space-age/solution.py index 92c502f54..5a350cf2f 100644 --- a/evaluator/datasets/polyglot_py/space-age/solution.py +++ b/evaluator/datasets/polyglot_py/space-age/solution.py @@ -6,7 +6,6 @@ def inner(self): class SpaceAge: - on_mercury = period_converter(7600530.24) on_venus = period_converter(19413907.2) on_earth = period_converter(31558149.76) diff --git a/evaluator/datasets/polyglot_py/spiral-matrix/solution.py b/evaluator/datasets/polyglot_py/spiral-matrix/solution.py index cc25fb3c2..0dc69ab89 100644 --- a/evaluator/datasets/polyglot_py/spiral-matrix/solution.py +++ b/evaluator/datasets/polyglot_py/spiral-matrix/solution.py @@ -1,5 +1,5 @@ def spiral_matrix(size): - matrix = [[0]*size for row in range(size)] + matrix = [[0] * size for row in range(size)] idx = 0 jdx = -1 element = 1 @@ -7,8 +7,8 @@ def spiral_matrix(size): digital = [0, 1, 0, -1] disco = [1, 0, -1, 0] - for edx in range(2*size - 1): - for _ in range((2*size - edx) // 2): + for edx in range(2 * size - 1): + for _ in range((2 * size - edx) // 2): idx += digital[edx % 4] jdx += disco[edx % 4] matrix[idx][jdx] = element diff --git a/evaluator/datasets/polyglot_py/square-root/solution.py b/evaluator/datasets/polyglot_py/square-root/solution.py index 94cd0fdc7..56c7ff401 100644 --- a/evaluator/datasets/polyglot_py/square-root/solution.py +++ b/evaluator/datasets/polyglot_py/square-root/solution.py @@ -1,5 +1,5 @@ def square_root(number): n = 0 - while n ** 2 != number: + while n**2 != number: n += 1 return n diff --git a/evaluator/datasets/polyglot_py/strain/main.py b/evaluator/datasets/polyglot_py/strain/main.py index 1d2a4e53b..d7bd3f0b3 100644 --- a/evaluator/datasets/polyglot_py/strain/main.py +++ b/evaluator/datasets/polyglot_py/strain/main.py @@ -1,6 +1,7 @@ -from typing import List, Callable, TypeVar +from typing import Callable, List, TypeVar + +T = TypeVar("T") -T = TypeVar('T') def keep(sequence: List[T], predicate: Callable[[T], bool]) -> List[T]: pass diff --git a/evaluator/datasets/polyglot_py/strain/solution.py b/evaluator/datasets/polyglot_py/strain/solution.py index 6c4b3f815..c4960684b 100644 --- a/evaluator/datasets/polyglot_py/strain/solution.py +++ b/evaluator/datasets/polyglot_py/strain/solution.py @@ -1,5 +1,6 @@ def keep(sequence, predicate): return [element for element in sequence if predicate(element)] + def discard(sequence, predicate): return [element for element in sequence if not predicate(element)] diff --git a/evaluator/datasets/polyglot_py/strain/tests.py b/evaluator/datasets/polyglot_py/strain/tests.py index 78c6337f2..7995f6eeb 100644 --- a/evaluator/datasets/polyglot_py/strain/tests.py +++ b/evaluator/datasets/polyglot_py/strain/tests.py @@ -1,6 +1,6 @@ import unittest -from main import keep, discard +from main import discard, keep class StrainTest(unittest.TestCase): @@ -22,25 +22,24 @@ def test_keep_everything(self): self.assertEqual(keep(inp, lambda x: x % 2 == 0), inp) def test_discard_endswith(self): - inp = ['dough', 'cash', 'plough', 'though', 'through', 'enough'] - out = ['cash'] - self.assertEqual(discard(inp, lambda x: str.endswith(x, 'ough')), out) + inp = ["dough", "cash", "plough", "though", "through", "enough"] + out = ["cash"] + self.assertEqual(discard(inp, lambda x: str.endswith(x, "ough")), out) def test_keep_z(self): - inp = ['zebra', 'arizona', 'apple', 'google', 'mozilla'] - out = ['zebra', 'arizona', 'mozilla'] - self.assertEqual(keep(inp, lambda x: 'z' in x), out) + inp = ["zebra", "arizona", "apple", "google", "mozilla"] + out = ["zebra", "arizona", "mozilla"] + self.assertEqual(keep(inp, lambda x: "z" in x), out) def test_keep_discard(self): - inp = ['1,2,3', 'one', 'almost!', 'love'] + inp = ["1,2,3", "one", "almost!", "love"] self.assertEqual(discard(keep(inp, str.isalpha), str.isalpha), []) def test_keep_plus_discard(self): - inp = ['1,2,3', 'one', 'almost!', 'love'] - out = ['one', 'love', '1,2,3', 'almost!'] - self.assertEqual( - keep(inp, str.isalpha) + discard(inp, str.isalpha), out) + inp = ["1,2,3", "one", "almost!", "love"] + out = ["one", "love", "1,2,3", "almost!"] + self.assertEqual(keep(inp, str.isalpha) + discard(inp, str.isalpha), out) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/sublist/tests.py b/evaluator/datasets/polyglot_py/sublist/tests.py index 15fc9b379..4a3cdfe5b 100644 --- a/evaluator/datasets/polyglot_py/sublist/tests.py +++ b/evaluator/datasets/polyglot_py/sublist/tests.py @@ -5,11 +5,11 @@ import unittest from main import ( - sublist, + EQUAL, SUBLIST, SUPERLIST, - EQUAL, UNEQUAL, + sublist, ) @@ -85,6 +85,4 @@ def test_large_lists(self): ) def test_spread_sublist(self): - self.assertEqual( - sublist(list(range(3, 200, 3)), list(range(15, 200, 15))), UNEQUAL - ) + self.assertEqual(sublist(list(range(3, 200, 3)), list(range(15, 200, 15))), UNEQUAL) diff --git a/evaluator/datasets/polyglot_py/sum-of-multiples/main.py b/evaluator/datasets/polyglot_py/sum-of-multiples/main.py index 5fc536d8e..b77905e7f 100644 --- a/evaluator/datasets/polyglot_py/sum-of-multiples/main.py +++ b/evaluator/datasets/polyglot_py/sum-of-multiples/main.py @@ -1,4 +1,5 @@ from typing import List + def sum_of_multiples(limit: int, multiples: List[int]) -> int: pass diff --git a/evaluator/datasets/polyglot_py/sum-of-multiples/solution.py b/evaluator/datasets/polyglot_py/sum-of-multiples/solution.py index 55e168227..41f3a685f 100644 --- a/evaluator/datasets/polyglot_py/sum-of-multiples/solution.py +++ b/evaluator/datasets/polyglot_py/sum-of-multiples/solution.py @@ -1,5 +1,2 @@ def sum_of_multiples(limit, multiples): - return sum(value for value in range(limit) - if any(value % multiple == 0 - for multiple in multiples - if multiple > 0)) + return sum(value for value in range(limit) if any(value % multiple == 0 for multiple in multiples if multiple > 0)) diff --git a/evaluator/datasets/polyglot_py/swift-scheduling/solution.py b/evaluator/datasets/polyglot_py/swift-scheduling/solution.py index 9f411819a..f36f39c21 100644 --- a/evaluator/datasets/polyglot_py/swift-scheduling/solution.py +++ b/evaluator/datasets/polyglot_py/swift-scheduling/solution.py @@ -4,54 +4,46 @@ def delivery_date(start, description): start_date = datetime.fromisoformat(start) - - if description == 'NOW': + if description == "NOW": due_date = start_date + timedelta(hours=2) - if description == 'ASAP': - if str(start_date.time()) < '13:00:00': + if description == "ASAP": + if str(start_date.time()) < "13:00:00": due_date = start_date.replace(hour=17, minute=0) else: - due_date = ( - start_date.replace(hour=13, minute=0) + - timedelta(days=1) - ) + due_date = start_date.replace(hour=13, minute=0) + timedelta(days=1) - if description =='EOW': + if description == "EOW": if start_date.isoweekday() < 4: - due_date = ( - start_date.replace(hour=17, minute=0) + - timedelta(days=5 - start_date.isoweekday()) - ) + due_date = start_date.replace(hour=17, minute=0) + timedelta(days=5 - start_date.isoweekday()) else: - due_date = ( - start_date.replace(hour=20, minute=0) + - timedelta(days=7 - start_date.isoweekday()) - ) + due_date = start_date.replace(hour=20, minute=0) + timedelta(days=7 - start_date.isoweekday()) - if description.endswith('M'): + if description.endswith("M"): month = int(description[:-1]) target = datetime(start_date.year, month, 1, 8, 0, 0) if start_date.month >= target.month: target = target.replace(year=target.year + 1) - if target.isoweekday() not in (6,7) and target.day in range(1, 8): + if target.isoweekday() not in (6, 7) and target.day in range(1, 8): due_date = target else: - if target.isoweekday() == 6: due_date = target + timedelta(days = 2) - if target.isoweekday() == 7: due_date = target + timedelta(days = 1) + if target.isoweekday() == 6: + due_date = target + timedelta(days=2) + if target.isoweekday() == 7: + due_date = target + timedelta(days=1) - if description.startswith('Q'): + if description.startswith("Q"): target = int(description[1:]) - current = ((start_date.month + 2) // 3) - month = {"Q1":4,"Q2": 7,"Q3": 10,"Q4": 1}[description] + current = (start_date.month + 2) // 3 + month = {"Q1": 4, "Q2": 7, "Q3": 10, "Q4": 1}[description] rollover = 1 if (current > target or target == 4) else 0 - due_date = start_date.replace( - start_date.year + rollover, month, 1, 8, 0, 0 - ) - timedelta(days=1) + due_date = start_date.replace(start_date.year + rollover, month, 1, 8, 0, 0) - timedelta(days=1) - if due_date.isoweekday() == 6: due_date -= timedelta(days=1) - if due_date.isoweekday() == 7: due_date -= timedelta(days=2) + if due_date.isoweekday() == 6: + due_date -= timedelta(days=1) + if due_date.isoweekday() == 7: + due_date -= timedelta(days=2) return due_date.isoformat() diff --git a/evaluator/datasets/polyglot_py/swift-scheduling/tests.py b/evaluator/datasets/polyglot_py/swift-scheduling/tests.py index 384af015b..3f115fad7 100644 --- a/evaluator/datasets/polyglot_py/swift-scheduling/tests.py +++ b/evaluator/datasets/polyglot_py/swift-scheduling/tests.py @@ -11,99 +11,67 @@ class SwiftSchedulingTest(unittest.TestCase): def test_now_translates_to_two_hours_later(self): - self.assertEqual( - delivery_date("2012-02-13T09:00:00", "NOW"), "2012-02-13T11:00:00" - ) + self.assertEqual(delivery_date("2012-02-13T09:00:00", "NOW"), "2012-02-13T11:00:00") def test_asap_before_one_in_the_afternoon_translates_to_today_at_five_in_the_afternoon( self, ): - self.assertEqual( - delivery_date("1999-06-03T09:45:00", "ASAP"), "1999-06-03T17:00:00" - ) + self.assertEqual(delivery_date("1999-06-03T09:45:00", "ASAP"), "1999-06-03T17:00:00") def test_asap_at_one_in_the_afternoon_translates_to_tomorrow_at_one_in_the_afternoon( self, ): - self.assertEqual( - delivery_date("2008-12-21T13:00:00", "ASAP"), "2008-12-22T13:00:00" - ) + self.assertEqual(delivery_date("2008-12-21T13:00:00", "ASAP"), "2008-12-22T13:00:00") def test_asap_after_one_in_the_afternoon_translates_to_tomorrow_at_one_in_the_afternoon( self, ): - self.assertEqual( - delivery_date("2008-12-21T14:50:00", "ASAP"), "2008-12-22T13:00:00" - ) + self.assertEqual(delivery_date("2008-12-21T14:50:00", "ASAP"), "2008-12-22T13:00:00") def test_eow_on_monday_translates_to_friday_at_five_in_the_afternoon(self): - self.assertEqual( - delivery_date("2025-02-03T16:00:00", "EOW"), "2025-02-07T17:00:00" - ) + self.assertEqual(delivery_date("2025-02-03T16:00:00", "EOW"), "2025-02-07T17:00:00") def test_eow_on_tuesday_translates_to_friday_at_five_in_the_afternoon(self): - self.assertEqual( - delivery_date("1997-04-29T10:50:00", "EOW"), "1997-05-02T17:00:00" - ) + self.assertEqual(delivery_date("1997-04-29T10:50:00", "EOW"), "1997-05-02T17:00:00") def test_eow_on_wednesday_translates_to_friday_at_five_in_the_afternoon(self): - self.assertEqual( - delivery_date("2005-09-14T11:00:00", "EOW"), "2005-09-16T17:00:00" - ) + self.assertEqual(delivery_date("2005-09-14T11:00:00", "EOW"), "2005-09-16T17:00:00") def test_eow_on_thursday_translates_to_sunday_at_eight_in_the_evening(self): - self.assertEqual( - delivery_date("2011-05-19T08:30:00", "EOW"), "2011-05-22T20:00:00" - ) + self.assertEqual(delivery_date("2011-05-19T08:30:00", "EOW"), "2011-05-22T20:00:00") def test_eow_on_friday_translates_to_sunday_at_eight_in_the_evening(self): - self.assertEqual( - delivery_date("2022-08-05T14:00:00", "EOW"), "2022-08-07T20:00:00" - ) + self.assertEqual(delivery_date("2022-08-05T14:00:00", "EOW"), "2022-08-07T20:00:00") def test_eow_translates_to_leap_day(self): - self.assertEqual( - delivery_date("2008-02-25T10:30:00", "EOW"), "2008-02-29T17:00:00" - ) + self.assertEqual(delivery_date("2008-02-25T10:30:00", "EOW"), "2008-02-29T17:00:00") def test_2_m_before_the_second_month_of_this_year_translates_to_the_first_workday_of_the_second_month_of_this_year( self, ): - self.assertEqual( - delivery_date("2007-01-02T14:15:00", "2M"), "2007-02-01T08:00:00" - ) + self.assertEqual(delivery_date("2007-01-02T14:15:00", "2M"), "2007-02-01T08:00:00") def test_11_m_in_the_eleventh_month_translates_to_the_first_workday_of_the_eleventh_month_of_next_year( self, ): - self.assertEqual( - delivery_date("2013-11-21T15:30:00", "11M"), "2014-11-03T08:00:00" - ) + self.assertEqual(delivery_date("2013-11-21T15:30:00", "11M"), "2014-11-03T08:00:00") def test_4_m_in_the_ninth_month_translates_to_the_first_workday_of_the_fourth_month_of_next_year( self, ): - self.assertEqual( - delivery_date("2019-11-18T15:15:00", "4M"), "2020-04-01T08:00:00" - ) + self.assertEqual(delivery_date("2019-11-18T15:15:00", "4M"), "2020-04-01T08:00:00") def test_q1_in_the_first_quarter_translates_to_the_last_workday_of_the_first_quarter_of_this_year( self, ): - self.assertEqual( - delivery_date("2003-01-01T10:45:00", "Q1"), "2003-03-31T08:00:00" - ) + self.assertEqual(delivery_date("2003-01-01T10:45:00", "Q1"), "2003-03-31T08:00:00") def test_q4_in_the_second_quarter_translates_to_the_last_workday_of_the_fourth_quarter_of_this_year( self, ): - self.assertEqual( - delivery_date("2001-04-09T09:00:00", "Q4"), "2001-12-31T08:00:00" - ) + self.assertEqual(delivery_date("2001-04-09T09:00:00", "Q4"), "2001-12-31T08:00:00") def test_q3_in_the_fourth_quarter_translates_to_the_last_workday_of_the_third_quarter_of_next_year( self, ): - self.assertEqual( - delivery_date("2022-10-06T11:00:00", "Q3"), "2023-09-29T08:00:00" - ) + self.assertEqual(delivery_date("2022-10-06T11:00:00", "Q3"), "2023-09-29T08:00:00") diff --git a/evaluator/datasets/polyglot_py/tournament/main.py b/evaluator/datasets/polyglot_py/tournament/main.py index 2c60c733e..7da03e173 100644 --- a/evaluator/datasets/polyglot_py/tournament/main.py +++ b/evaluator/datasets/polyglot_py/tournament/main.py @@ -1,4 +1,5 @@ from typing import List + def tally(rows: List[str]) -> List[str]: pass diff --git a/evaluator/datasets/polyglot_py/tournament/solution.py b/evaluator/datasets/polyglot_py/tournament/solution.py index e711468e2..c26f961a5 100644 --- a/evaluator/datasets/polyglot_py/tournament/solution.py +++ b/evaluator/datasets/polyglot_py/tournament/solution.py @@ -12,7 +12,7 @@ def invert_result(result): def parse_game(game_line): - game = game_line.split(';') + game = game_line.split(";") if len(game) == 3 and game[2] in RESULTS: result = RESULTS[game[2]] return (game[0], result), (game[1], invert_result(result)) @@ -24,13 +24,11 @@ def calculate_points(stats): def format_table(results): - table = ['Team | MP | W | D | L | P'] + table = ["Team | MP | W | D | L | P"] - for team, games in sorted( - results.items(), key=lambda group: (-calculate_points(group[1]), group[0])): - team_fmt = '{0:30} | {1:2} | {3:2} | {4:2} | {5:2} | {2:2}' - table.append( - team_fmt.format(team, sum(games), calculate_points(games), *games)) + for team, games in sorted(results.items(), key=lambda group: (-calculate_points(group[1]), group[0])): + team_fmt = "{0:30} | {1:2} | {3:2} | {4:2} | {5:2} | {2:2}" + table.append(team_fmt.format(team, sum(games), calculate_points(games), *games)) return table diff --git a/evaluator/datasets/polyglot_py/transpose/solution.py b/evaluator/datasets/polyglot_py/transpose/solution.py index cbb8e5f96..fb35092f7 100644 --- a/evaluator/datasets/polyglot_py/transpose/solution.py +++ b/evaluator/datasets/polyglot_py/transpose/solution.py @@ -1,6 +1,6 @@ def transpose(lines): - rows = [row.replace(' ', '_') for row in lines.splitlines()] + rows = [row.replace(" ", "_") for row in lines.splitlines()] rows = [row.ljust(len(max(rows, key=len))) for row in rows] - rows = [''.join(row) for row in zip(*rows)] - rows = [row.rstrip().replace('_', ' ') for row in rows] - return '\n'.join(rows) + rows = ["".join(row) for row in zip(*rows)] + rows = [row.rstrip().replace("_", " ") for row in rows] + return "\n".join(rows) diff --git a/evaluator/datasets/polyglot_py/tree-building/main.py b/evaluator/datasets/polyglot_py/tree-building/main.py index bf109e9bb..d7b82deee 100644 --- a/evaluator/datasets/polyglot_py/tree-building/main.py +++ b/evaluator/datasets/polyglot_py/tree-building/main.py @@ -20,9 +20,9 @@ def BuildTree(records: list[Record]) -> Node | None: ordered_id = [i.record_id for i in records] if records: if ordered_id[-1] != len(ordered_id) - 1: - raise ValueError('Record id is invalid or out of order.') + raise ValueError("Record id is invalid or out of order.") if ordered_id[0] != 0: - raise ValueError('Record id is invalid or out of order.') + raise ValueError("Record id is invalid or out of order.") trees = [] parent = {} for i in range(len(ordered_id)): @@ -35,7 +35,7 @@ def BuildTree(records: list[Record]) -> Node | None: raise ValueError("Node parent_id should be smaller than it's record_id.") if j.record_id == j.parent_id: if j.record_id != 0: - raise ValueError('Only root should have equal record and parent id.') + raise ValueError("Only root should have equal record and parent id.") trees.append(Node(ordered_id[i])) for i in range(len(ordered_id)): for j in trees: diff --git a/evaluator/datasets/polyglot_py/tree-building/solution.py b/evaluator/datasets/polyglot_py/tree-building/solution.py index e3929ea03..2c39609da 100644 --- a/evaluator/datasets/polyglot_py/tree-building/solution.py +++ b/evaluator/datasets/polyglot_py/tree-building/solution.py @@ -15,7 +15,7 @@ def __init__(self, node_id): def validate_record(record): if record.equal_id() and record.record_id != 0: - raise ValueError('Only root should have equal record and parent id.') + raise ValueError("Only root should have equal record and parent id.") if not record.equal_id() and record.parent_id >= record.record_id: raise ValueError("Node parent_id should be smaller than it's record_id.") @@ -36,7 +36,7 @@ def BuildTree(records): for index, record_id in enumerate(ordered_id): if index != record_id: - raise ValueError('Record id is invalid or out of order.') + raise ValueError("Record id is invalid or out of order.") if record_id == root_id: root = node_dict[record_id] diff --git a/evaluator/datasets/polyglot_py/tree-building/tests.py b/evaluator/datasets/polyglot_py/tree-building/tests.py index 94db31855..bbcf1734f 100644 --- a/evaluator/datasets/polyglot_py/tree-building/tests.py +++ b/evaluator/datasets/polyglot_py/tree-building/tests.py @@ -1,14 +1,14 @@ import unittest -from main import Record, BuildTree +from main import BuildTree, Record class TreeBuildingTest(unittest.TestCase): """ - Record(record_id, parent_id): records given to be processed - Node(node_id): Node in tree - BuildTree(records): records as argument and returns tree - BuildTree should raise ValueError if given records are invalid + Record(record_id, parent_id): records given to be processed + Node(node_id): Node in tree + BuildTree(records): records as argument and returns tree + BuildTree should raise ValueError if given records are invalid """ def test_empty_list_input(self): @@ -17,19 +17,13 @@ def test_empty_list_input(self): self.assertIsNone(root) def test_one_node(self): - records = [ - Record(0, 0) - ] + records = [Record(0, 0)] root = BuildTree(records) self.assert_node_is_leaf(root, node_id=0) def test_three_nodes_in_order(self): - records = [ - Record(0, 0), - Record(1, 0), - Record(2, 0) - ] + records = [Record(0, 0), Record(1, 0), Record(2, 0)] root = BuildTree(records) self.assert_node_is_branch(root, node_id=0, children_count=2) @@ -37,11 +31,7 @@ def test_three_nodes_in_order(self): self.assert_node_is_leaf(root.children[1], node_id=2) def test_three_nodes_in_reverse_order(self): - records = [ - Record(2, 0), - Record(1, 0), - Record(0, 0) - ] + records = [Record(2, 0), Record(1, 0), Record(0, 0)] root = BuildTree(records) self.assert_node_is_branch(root, node_id=0, children_count=2) @@ -49,12 +39,7 @@ def test_three_nodes_in_reverse_order(self): self.assert_node_is_leaf(root.children[1], node_id=2) def test_more_than_two_children(self): - records = [ - Record(0, 0), - Record(1, 0), - Record(2, 0), - Record(3, 0) - ] + records = [Record(0, 0), Record(1, 0), Record(2, 0), Record(3, 0)] root = BuildTree(records) self.assert_node_is_branch(root, node_id=0, children_count=3) @@ -63,15 +48,7 @@ def test_more_than_two_children(self): self.assert_node_is_leaf(root.children[2], node_id=3) def test_binary_tree(self): - records = [ - Record(6, 2), - Record(0, 0), - Record(3, 1), - Record(2, 0), - Record(4, 1), - Record(5, 2), - Record(1, 0) - ] + records = [Record(6, 2), Record(0, 0), Record(3, 1), Record(2, 0), Record(4, 1), Record(5, 2), Record(1, 0)] root = BuildTree(records) self.assert_node_is_branch(root, 0, 2) @@ -103,10 +80,7 @@ def test_unbalanced_tree(self): self.assert_node_is_leaf(root.children[1].children[0], 6) def test_root_node_has_parent(self): - records = [ - Record(0, 1), - Record(1, 0) - ] + records = [Record(0, 1), Record(1, 0)] # Root parent_id should be equal to record_id(0) with self.assertRaises(ValueError) as err: BuildTree(records) @@ -114,10 +88,7 @@ def test_root_node_has_parent(self): self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than it's record_id.") def test_no_root_node(self): - records = [ - Record(1, 0), - Record(2, 0) - ] + records = [Record(1, 0), Record(2, 0)] # Record with record_id 0 (root) is missing with self.assertRaises(ValueError) as err: BuildTree(records) @@ -125,12 +96,7 @@ def test_no_root_node(self): self.assertEqual(err.exception.args[0], "Record id is invalid or out of order.") def test_non_continuous(self): - records = [ - Record(2, 0), - Record(4, 2), - Record(1, 0), - Record(0, 0) - ] + records = [Record(2, 0), Record(4, 2), Record(1, 0), Record(0, 0)] # Record with record_id 3 is missing with self.assertRaises(ValueError) as err: BuildTree(records) @@ -138,15 +104,7 @@ def test_non_continuous(self): self.assertEqual(err.exception.args[0], "Record id is invalid or out of order.") def test_cycle_directly(self): - records = [ - Record(5, 2), - Record(3, 2), - Record(2, 2), - Record(4, 1), - Record(1, 0), - Record(0, 0), - Record(6, 3) - ] + records = [Record(5, 2), Record(3, 2), Record(2, 2), Record(4, 1), Record(1, 0), Record(0, 0), Record(6, 3)] # Cycle caused by Record 2 with parent_id pointing to itself with self.assertRaises(ValueError) as err: BuildTree(records) @@ -154,15 +112,7 @@ def test_cycle_directly(self): self.assertEqual(err.exception.args[0], "Only root should have equal record and parent id.") def test_cycle_indirectly(self): - records = [ - Record(5, 2), - Record(3, 2), - Record(2, 6), - Record(4, 1), - Record(1, 0), - Record(0, 0), - Record(6, 3) - ] + records = [Record(5, 2), Record(3, 2), Record(2, 6), Record(4, 1), Record(1, 0), Record(0, 0), Record(6, 3)] # Cycle caused by Record 2 with parent_id(6) greater than record_id(2) with self.assertRaises(ValueError) as err: BuildTree(records) @@ -170,11 +120,7 @@ def test_cycle_indirectly(self): self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than it's record_id.") def test_higher_id_parent_of_lower_id(self): - records = [ - Record(0, 0), - Record(2, 0), - Record(1, 2) - ] + records = [Record(0, 0), Record(2, 0), Record(1, 2)] # Record 1 have parent_id(2) greater than record_id(1) with self.assertRaises(ValueError) as err: BuildTree(records) diff --git a/evaluator/datasets/polyglot_py/triangle/main.py b/evaluator/datasets/polyglot_py/triangle/main.py index 08ebbb55a..18fd1114c 100644 --- a/evaluator/datasets/polyglot_py/triangle/main.py +++ b/evaluator/datasets/polyglot_py/triangle/main.py @@ -1,5 +1,6 @@ from typing import List + def equilateral(sides: List[float]) -> bool: pass diff --git a/evaluator/datasets/polyglot_py/triangle/solution.py b/evaluator/datasets/polyglot_py/triangle/solution.py index 10c064649..4636187c5 100644 --- a/evaluator/datasets/polyglot_py/triangle/solution.py +++ b/evaluator/datasets/polyglot_py/triangle/solution.py @@ -1,7 +1,5 @@ def valid(sides): - return sum(sorted(sides)[:2]) >= sorted(sides)[2] and all( - side > 0 for side in sides - ) + return sum(sorted(sides)[:2]) >= sorted(sides)[2] and all(side > 0 for side in sides) def equilateral(sides): @@ -9,9 +7,7 @@ def equilateral(sides): def isosceles(sides): - return valid(sides) and any( - side_1 == side_2 for side_1, side_2 in zip(sorted(sides), sorted(sides)[1:]) - ) + return valid(sides) and any(side_1 == side_2 for side_1, side_2 in zip(sorted(sides), sorted(sides)[1:])) def scalene(sides): diff --git a/evaluator/datasets/polyglot_py/trinary/solution.py b/evaluator/datasets/polyglot_py/trinary/solution.py index 48074c008..c497c8ed4 100644 --- a/evaluator/datasets/polyglot_py/trinary/solution.py +++ b/evaluator/datasets/polyglot_py/trinary/solution.py @@ -2,6 +2,6 @@ def trinary(string): - if set(string) - set('012'): + if set(string) - set("012"): return 0 return reduce(lambda idx, edx: idx * 3 + int(edx), string, 0) diff --git a/evaluator/datasets/polyglot_py/trinary/tests.py b/evaluator/datasets/polyglot_py/trinary/tests.py index 3e92d2d01..4881d500a 100644 --- a/evaluator/datasets/polyglot_py/trinary/tests.py +++ b/evaluator/datasets/polyglot_py/trinary/tests.py @@ -5,26 +5,26 @@ class TrinaryTest(unittest.TestCase): def test_valid_trinary1(self): - self.assertEqual(trinary('0'), 0) + self.assertEqual(trinary("0"), 0) def test_valid_trinary2(self): - self.assertEqual(trinary('1'), 1) + self.assertEqual(trinary("1"), 1) def test_valid_trinary3(self): - self.assertEqual(trinary('10'), 3) + self.assertEqual(trinary("10"), 3) def test_valid_trinary4(self): - self.assertEqual(trinary('102101'), 307) + self.assertEqual(trinary("102101"), 307) def test_valid_trinary5(self): - self.assertEqual(trinary('22222'), 242) + self.assertEqual(trinary("22222"), 242) def test_valid_trinary6(self): - self.assertEqual(trinary('10000'), 81) + self.assertEqual(trinary("10000"), 81) def test_invalid_trinary(self): - self.assertEqual(trinary('13201'), 0) + self.assertEqual(trinary("13201"), 0) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py/twelve-days/main.py b/evaluator/datasets/polyglot_py/twelve-days/main.py index 7252929f5..3bfa905b5 100644 --- a/evaluator/datasets/polyglot_py/twelve-days/main.py +++ b/evaluator/datasets/polyglot_py/twelve-days/main.py @@ -1,4 +1,5 @@ from typing import List + def recite(start_verse: int, end_verse: int) -> List[str]: pass diff --git a/evaluator/datasets/polyglot_py/twelve-days/solution.py b/evaluator/datasets/polyglot_py/twelve-days/solution.py index 6b24c65b1..e7a2054e3 100644 --- a/evaluator/datasets/polyglot_py/twelve-days/solution.py +++ b/evaluator/datasets/polyglot_py/twelve-days/solution.py @@ -1,28 +1,43 @@ -GIFTS = ['twelve Drummers Drumming', - 'eleven Pipers Piping', - 'ten Lords-a-Leaping', - 'nine Ladies Dancing', - 'eight Maids-a-Milking', - 'seven Swans-a-Swimming', - 'six Geese-a-Laying', - 'five Gold Rings', - 'four Calling Birds', - 'three French Hens', - 'two Turtle Doves', - 'a Partridge in a Pear Tree'] +GIFTS = [ + "twelve Drummers Drumming", + "eleven Pipers Piping", + "ten Lords-a-Leaping", + "nine Ladies Dancing", + "eight Maids-a-Milking", + "seven Swans-a-Swimming", + "six Geese-a-Laying", + "five Gold Rings", + "four Calling Birds", + "three French Hens", + "two Turtle Doves", + "a Partridge in a Pear Tree", +] -ORDINAL = [None, 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', - 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth'] +ORDINAL = [ + None, + "first", + "second", + "third", + "fourth", + "fifth", + "sixth", + "seventh", + "eighth", + "ninth", + "tenth", + "eleventh", + "twelfth", +] def verse(day_number): gifts = GIFTS[-day_number:] if len(gifts) > 1: - gifts[:-1] = [', '.join(gifts[:-1])] + gifts[:-1] = [", ".join(gifts[:-1])] - gifts = ', and '.join(gifts) - return f'On the {ORDINAL[day_number]} day of Christmas my true love gave to me: {gifts}.' + gifts = ", and ".join(gifts) + return f"On the {ORDINAL[day_number]} day of Christmas my true love gave to me: {gifts}." def recite(start, end): diff --git a/evaluator/datasets/polyglot_py/twelve-days/tests.py b/evaluator/datasets/polyglot_py/twelve-days/tests.py index e606bf22b..2d11b5158 100644 --- a/evaluator/datasets/polyglot_py/twelve-days/tests.py +++ b/evaluator/datasets/polyglot_py/twelve-days/tests.py @@ -15,17 +15,12 @@ class TwelveDaysTest(unittest.TestCase): def test_first_day_a_partridge_in_a_pear_tree(self): - expected = [ - "On the first day of Christmas my true love gave to me: " - "a Partridge in a Pear Tree." - ] + expected = ["On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree."] self.assertEqual(recite(1, 1), expected) def test_second_day_two_turtle_doves(self): expected = [ - "On the second day of Christmas my true love gave to me: " - "two Turtle Doves, " - "and a Partridge in a Pear Tree." + "On the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree." ] self.assertEqual(recite(2, 2), expected) diff --git a/evaluator/datasets/polyglot_py/two-bucket/solution.py b/evaluator/datasets/polyglot_py/two-bucket/solution.py index 83077eb16..6fa8ececd 100644 --- a/evaluator/datasets/polyglot_py/two-bucket/solution.py +++ b/evaluator/datasets/polyglot_py/two-bucket/solution.py @@ -1,13 +1,13 @@ -''' - This solution implements a breadth-first search of the graph - of possible valid states for the two buckets until it reaches a state - in which one of the two buckets contains the goal amount -''' +""" +This solution implements a breadth-first search of the graph +of possible valid states for the two buckets until it reaches a state +in which one of the two buckets contains the goal amount +""" def measure(bucket_one, bucket_two, goal, start_bucket): sizes = [bucket_one, bucket_two] - goal_index = 0 if start_bucket == 'one' else 1 + goal_index = 0 if start_bucket == "one" else 1 def empty(buckets, idx): return [0, buckets[1]] if idx == 0 else [buckets[0], 0] @@ -22,7 +22,7 @@ def consolidate(buckets, idx): return [target, source] if idx == 0 else [source, target] def bucket_str(buckets): - return f'{buckets[0]},{buckets[1]}' + return f"{buckets[0]},{buckets[1]}" invalid = [0, 0] invalid[1 - goal_index] = sizes[1 - goal_index] @@ -44,10 +44,10 @@ def bucket_str(buckets): to_visit.append((fill(buckets, idx), number_count)) to_visit.append((consolidate(buckets, idx), number_count)) if not any(to_visit): - raise ValueError('No more moves!') + raise ValueError("No more moves!") buckets, count = to_visit.pop(0) goal_index = buckets.index(goal) - goal_bucket = ['one', 'two'][goal_index] + goal_bucket = ["one", "two"][goal_index] other_bucket = buckets[1 - goal_index] return (count, goal_bucket, other_bucket) diff --git a/evaluator/datasets/polyglot_py/two-fer/solution.py b/evaluator/datasets/polyglot_py/two-fer/solution.py index 425d236d2..95d3c99cf 100644 --- a/evaluator/datasets/polyglot_py/two-fer/solution.py +++ b/evaluator/datasets/polyglot_py/two-fer/solution.py @@ -1,2 +1,2 @@ -def two_fer(name='you'): - return f'One for {name}, one for me.' +def two_fer(name="you"): + return f"One for {name}, one for me." diff --git a/evaluator/datasets/polyglot_py/variable-length-quantity/solution.py b/evaluator/datasets/polyglot_py/variable-length-quantity/solution.py index ed5a393b6..0f6a2f43b 100644 --- a/evaluator/datasets/polyglot_py/variable-length-quantity/solution.py +++ b/evaluator/datasets/polyglot_py/variable-length-quantity/solution.py @@ -1,5 +1,5 @@ EIGHT_BIT_MASK = 0x80 -SEVEN_BIT_MASK = 0x7f +SEVEN_BIT_MASK = 0x7F def encode_single(number): @@ -23,12 +23,12 @@ def decode(byte_string): for idx, byte in enumerate(byte_string): number <<= 7 - number += (byte & SEVEN_BIT_MASK) + number += byte & SEVEN_BIT_MASK if byte & EIGHT_BIT_MASK == 0: values.append(number) number = 0 elif idx == len(byte_string) - 1: - raise ValueError('incomplete sequence') + raise ValueError("incomplete sequence") return values diff --git a/evaluator/datasets/polyglot_py/variable-length-quantity/tests.py b/evaluator/datasets/polyglot_py/variable-length-quantity/tests.py index 1bdf83a4c..dde636ca8 100644 --- a/evaluator/datasets/polyglot_py/variable-length-quantity/tests.py +++ b/evaluator/datasets/polyglot_py/variable-length-quantity/tests.py @@ -60,9 +60,7 @@ def test_two_single_byte_values(self): self.assertEqual(encode([0x40, 0x7F]), [0x40, 0x7F]) def test_two_multi_byte_values(self): - self.assertEqual( - encode([0x4000, 0x123456]), [0x81, 0x80, 0x0, 0xC8, 0xE8, 0x56] - ) + self.assertEqual(encode([0x4000, 0x123456]), [0x81, 0x80, 0x0, 0xC8, 0xE8, 0x56]) def test_many_multi_byte_values(self): self.assertEqual( diff --git a/evaluator/datasets/polyglot_py/word-count/main.py b/evaluator/datasets/polyglot_py/word-count/main.py index 81b035fa1..ca865514a 100644 --- a/evaluator/datasets/polyglot_py/word-count/main.py +++ b/evaluator/datasets/polyglot_py/word-count/main.py @@ -1,4 +1,5 @@ from typing import Dict + def count_words(sentence: str) -> Dict[str, int]: pass diff --git a/evaluator/datasets/polyglot_py/word-count/solution.py b/evaluator/datasets/polyglot_py/word-count/solution.py index 03fb2608a..d7c710c7b 100644 --- a/evaluator/datasets/polyglot_py/word-count/solution.py +++ b/evaluator/datasets/polyglot_py/word-count/solution.py @@ -1,7 +1,6 @@ import re from collections import Counter - WORDS = re.compile("[a-z0-9]+(['][a-z]+)?") diff --git a/evaluator/datasets/polyglot_py/word-count/tests.py b/evaluator/datasets/polyglot_py/word-count/tests.py index 38d0d08fa..52796e9fd 100644 --- a/evaluator/datasets/polyglot_py/word-count/tests.py +++ b/evaluator/datasets/polyglot_py/word-count/tests.py @@ -26,9 +26,7 @@ def test_handles_cramped_lists(self): self.assertEqual(count_words("one,two,three"), {"one": 1, "two": 1, "three": 1}) def test_handles_expanded_lists(self): - self.assertEqual( - count_words("one,\ntwo,\nthree"), {"one": 1, "two": 1, "three": 1} - ) + self.assertEqual(count_words("one,\ntwo,\nthree"), {"one": 1, "two": 1, "three": 1}) def test_ignore_punctuation(self): self.assertEqual( @@ -37,9 +35,7 @@ def test_ignore_punctuation(self): ) def test_include_numbers(self): - self.assertEqual( - count_words("testing, 1, 2 testing"), {"testing": 2, "1": 1, "2": 1} - ) + self.assertEqual(count_words("testing, 1, 2 testing"), {"testing": 2, "1": 1, "2": 1}) def test_normalize_case(self): self.assertEqual(count_words("go Go GO Stop stop"), {"go": 3, "stop": 2}) @@ -81,14 +77,10 @@ def test_substrings_from_the_beginning(self): ) def test_multiple_spaces_not_detected_as_a_word(self): - self.assertEqual( - count_words(" multiple whitespaces"), {"multiple": 1, "whitespaces": 1} - ) + self.assertEqual(count_words(" multiple whitespaces"), {"multiple": 1, "whitespaces": 1}) def test_alternating_word_separators_not_detected_as_a_word(self): - self.assertEqual( - count_words(",\n,one,\n ,two \n 'three'"), {"one": 1, "two": 1, "three": 1} - ) + self.assertEqual(count_words(",\n,one,\n ,two \n 'three'"), {"one": 1, "two": 1, "three": 1}) def test_quotation_for_word_with_apostrophe(self): self.assertEqual(count_words("can, can't, 'can't'"), {"can": 1, "can't": 2}) @@ -97,9 +89,7 @@ def test_quotation_for_word_with_apostrophe(self): def test_tabs(self): self.assertEqual( - count_words( - "rah rah ah ah ah roma roma ma ga ga oh la la want your bad romance" - ), + count_words("rah rah ah ah ah roma roma ma ga ga oh la la want your bad romance"), { "rah": 2, "ah": 3, diff --git a/evaluator/datasets/polyglot_py/word-search/main.py b/evaluator/datasets/polyglot_py/word-search/main.py index 5fc5a89ff..14cf70027 100644 --- a/evaluator/datasets/polyglot_py/word-search/main.py +++ b/evaluator/datasets/polyglot_py/word-search/main.py @@ -1,5 +1,6 @@ from typing import List, Optional, Tuple + class Point: def __init__(self, x: int, y: int) -> None: self.x = None diff --git a/evaluator/datasets/polyglot_py/word-search/solution.py b/evaluator/datasets/polyglot_py/word-search/solution.py index fe2b9b665..f61487133 100644 --- a/evaluator/datasets/polyglot_py/word-search/solution.py +++ b/evaluator/datasets/polyglot_py/word-search/solution.py @@ -7,7 +7,7 @@ def __init__(self, x, y): self.y = y def __repr__(self): - return f'Point({self.x}:{self.y})' + return f"Point({self.x}:{self.y})" def __add__(self, other): return Point(self.x + other.x, self.y + other.y) @@ -22,8 +22,16 @@ def __ne__(self, other): return not self == other -DIRECTIONS = (Point(1, 0), Point(1, -1), Point(1, 1), Point(-1, -1), - Point(0, -1), Point(0, 1), Point(-1, 1), Point(-1, 0)) +DIRECTIONS = ( + Point(1, 0), + Point(1, -1), + Point(1, 1), + Point(-1, -1), + Point(0, -1), + Point(0, 1), + Point(-1, 1), + Point(-1, 0), +) class WordSearch: @@ -48,8 +56,7 @@ def find(self, word, position, direction): return position, current - direction def search(self, word): - positions = (Point(idx, edx) - for idx in range(self.width) for edx in range(self.height)) + positions = (Point(idx, edx) for idx in range(self.width) for edx in range(self.height)) for position in positions: for direction in DIRECTIONS: result = self.find(word, position, direction) diff --git a/evaluator/datasets/polyglot_py/word-search/tests.py b/evaluator/datasets/polyglot_py/word-search/tests.py index 0d2838a6f..8d0f650e4 100644 --- a/evaluator/datasets/polyglot_py/word-search/tests.py +++ b/evaluator/datasets/polyglot_py/word-search/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - WordSearch, Point, + WordSearch, ) diff --git a/evaluator/datasets/polyglot_py/wordy/solution.py b/evaluator/datasets/polyglot_py/wordy/solution.py index 2488153e8..484535783 100644 --- a/evaluator/datasets/polyglot_py/wordy/solution.py +++ b/evaluator/datasets/polyglot_py/wordy/solution.py @@ -1,16 +1,15 @@ from operator import add, mul, sub from operator import floordiv as div - -VALID_OPERATIONS = {'plus': add, 'minus': sub, 'multiplied by': mul, 'divided by': div} +VALID_OPERATIONS = {"plus": add, "minus": sub, "multiplied by": mul, "divided by": div} def answer(question): if not bool(question[8:-1].strip().lower().split()): - raise ValueError('syntax error') + raise ValueError("syntax error") - elif not question.startswith('What is '): - raise ValueError('unknown operation') + elif not question.startswith("What is "): + raise ValueError("unknown operation") else: words = question[8:-1].strip().lower().split() @@ -19,7 +18,7 @@ def answer(question): try: main_value = int(words.pop()) except ValueError as error: - raise ValueError('syntax error') from error + raise ValueError("syntax error") from error while words: operation = [words.pop()] @@ -30,20 +29,20 @@ def answer(question): break except ValueError as error: if next_to_evaluate == operation[-1]: - raise ValueError('syntax error') from error + raise ValueError("syntax error") from error else: operation.append(next_to_evaluate) else: - if operation[-1] not in VALID_OPERATIONS and not operation[-1].isdigit() : - raise ValueError('unknown operation') + if operation[-1] not in VALID_OPERATIONS and not operation[-1].isdigit(): + raise ValueError("unknown operation") else: - raise ValueError('syntax error') + raise ValueError("syntax error") - operation = ' '.join(operation) + operation = " ".join(operation) try: main_value = VALID_OPERATIONS[operation](main_value, second_value) except KeyError as error: - raise ValueError('syntax error') from error + raise ValueError("syntax error") from error return main_value diff --git a/evaluator/datasets/polyglot_py/yacht/solution.py b/evaluator/datasets/polyglot_py/yacht/solution.py index dedcb7ac8..a290caf97 100644 --- a/evaluator/datasets/polyglot_py/yacht/solution.py +++ b/evaluator/datasets/polyglot_py/yacht/solution.py @@ -62,4 +62,4 @@ def score(dice, category): try: return functions[category](dice) except IndexError as error: - raise ValueError('No such category.') from error + raise ValueError("No such category.") from error diff --git a/evaluator/datasets/polyglot_py/yacht/tests.py b/evaluator/datasets/polyglot_py/yacht/tests.py index 2a587b17d..864de05cd 100644 --- a/evaluator/datasets/polyglot_py/yacht/tests.py +++ b/evaluator/datasets/polyglot_py/yacht/tests.py @@ -3,6 +3,7 @@ # File last updated on 2023-07-19 import unittest + import main as yacht diff --git a/evaluator/datasets/polyglot_py/zebra-puzzle/solution.py b/evaluator/datasets/polyglot_py/zebra-puzzle/solution.py index a6ea8f937..f9385124b 100644 --- a/evaluator/datasets/polyglot_py/zebra-puzzle/solution.py +++ b/evaluator/datasets/polyglot_py/zebra-puzzle/solution.py @@ -25,25 +25,36 @@ def solution(): # - J08K <3 (1:05 AM, nov 29th, 2021) result = next( - [{ - english_man: 'Englishman', - spaniard: 'Spaniard', - ukrainian: 'Ukrainian', - japanese: 'Japanese', - norwegian: 'Norwegian' - }[idx] for idx in (water, zebra)] + [ + { + english_man: "Englishman", + spaniard: "Spaniard", + ukrainian: "Ukrainian", + japanese: "Japanese", + norwegian: "Norwegian", + }[idx] + for idx in (water, zebra) + ] for (red, green, ivory, yellow, blue) in orderings if just_right_of(green, ivory) for (english_man, spaniard, ukrainian, japanese, norwegian) in orderings - if english_man is red if norwegian is first if next_to(norwegian, blue) - for (coffee, tea, milk, orange_juice, water) in orderings if coffee is green - if ukrainian is tea if milk is middle - for (old_gold, kools, chesterfields, lucky_strike, parliaments - ) in orderings if kools is yellow if lucky_strike is orange_juice + if english_man is red + if norwegian is first + if next_to(norwegian, blue) + for (coffee, tea, milk, orange_juice, water) in orderings + if coffee is green + if ukrainian is tea + if milk is middle + for (old_gold, kools, chesterfields, lucky_strike, parliaments) in orderings + if kools is yellow + if lucky_strike is orange_juice if japanese is parliaments - for (dog, snails, fox, horse, zebra) in orderings if spaniard is dog - if old_gold is snails if next_to(chesterfields, fox) - if next_to(kools, horse)) + for (dog, snails, fox, horse, zebra) in orderings + if spaniard is dog + if old_gold is snails + if next_to(chesterfields, fox) + if next_to(kools, horse) + ) return result diff --git a/evaluator/datasets/polyglot_py/zipper/solution.py b/evaluator/datasets/polyglot_py/zipper/solution.py index 570771ddb..8fdeb2e52 100644 --- a/evaluator/datasets/polyglot_py/zipper/solution.py +++ b/evaluator/datasets/polyglot_py/zipper/solution.py @@ -8,28 +8,28 @@ def __init__(self, tree, ancestors): self.ancestors = ancestors def value(self): - return self.tree['value'] + return self.tree["value"] def set_value(self, value): - self.tree['value'] = value + self.tree["value"] = value return self def left(self): - if self.tree['left'] is None: + if self.tree["left"] is None: return None - return Zipper(self.tree['left'], self.ancestors + [self.tree]) + return Zipper(self.tree["left"], self.ancestors + [self.tree]) def set_left(self, tree): - self.tree['left'] = tree + self.tree["left"] = tree return self def right(self): - if self.tree['right'] is None: + if self.tree["right"] is None: return None - return Zipper(self.tree['right'], self.ancestors + [self.tree]) + return Zipper(self.tree["right"], self.ancestors + [self.tree]) def set_right(self, tree): - self.tree['right'] = tree + self.tree["right"] = tree return self def up(self): diff --git a/evaluator/datasets/polyglot_py/zipper/tests.py b/evaluator/datasets/polyglot_py/zipper/tests.py index 9c7ef27ab..8bfa330ac 100644 --- a/evaluator/datasets/polyglot_py/zipper/tests.py +++ b/evaluator/datasets/polyglot_py/zipper/tests.py @@ -207,9 +207,7 @@ def test_set_left_with_leaf(self): } zipper = Zipper.from_tree(initial) - result = ( - zipper.left().set_left({"value": 5, "left": None, "right": None}).to_tree() - ) + result = zipper.left().set_left({"value": 5, "left": None, "right": None}).to_tree() self.assertEqual(result, expected) def test_set_right_with_null(self): diff --git a/evaluator/datasets/polyglot_py_unpatched/accumulate/tests.py b/evaluator/datasets/polyglot_py_unpatched/accumulate/tests.py index 879983286..44a226436 100644 --- a/evaluator/datasets/polyglot_py_unpatched/accumulate/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/accumulate/tests.py @@ -8,32 +8,23 @@ def test_empty_sequence(self): self.assertEqual(accumulate([], lambda x: x / 2), []) def test_pow(self): - self.assertEqual( - accumulate([1, 2, 3, 4, 5], lambda x: x * x), [1, 4, 9, 16, 25]) + self.assertEqual(accumulate([1, 2, 3, 4, 5], lambda x: x * x), [1, 4, 9, 16, 25]) def test_divmod(self): - self.assertEqual( - accumulate([10, 17, 23], lambda x: divmod(x, 7)), - [(1, 3), (2, 3), (3, 2)]) + self.assertEqual(accumulate([10, 17, 23], lambda x: divmod(x, 7)), [(1, 3), (2, 3), (3, 2)]) def test_composition(self): inp = [10, 17, 23] - self.assertEqual( - accumulate( - accumulate(inp, lambda x: divmod(x, 7)), - lambda x: 7 * x[0] + x[1]), inp) + self.assertEqual(accumulate(accumulate(inp, lambda x: divmod(x, 7)), lambda x: 7 * x[0] + x[1]), inp) def test_capitalize(self): - self.assertEqual( - accumulate(['hello', 'world'], str.upper), ['HELLO', 'WORLD']) + self.assertEqual(accumulate(["hello", "world"], str.upper), ["HELLO", "WORLD"]) def test_recursive(self): - inp = ['a', 'b', 'c'] - out = [['a1', 'a2', 'a3'], ['b1', 'b2', 'b3'], ['c1', 'c2', 'c3']] - self.assertEqual( - accumulate( - inp, lambda x: accumulate(list('123'), lambda y: x + y)), out) + inp = ["a", "b", "c"] + out = [["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]] + self.assertEqual(accumulate(inp, lambda x: accumulate(list("123"), lambda y: x + y)), out) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/acronym/solution.py b/evaluator/datasets/polyglot_py_unpatched/acronym/solution.py index e7cf298dc..51ed1d618 100644 --- a/evaluator/datasets/polyglot_py_unpatched/acronym/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/acronym/solution.py @@ -3,4 +3,4 @@ def abbreviate(words): regex = "[A-Z]+['a-z]*|['a-z]+" - return ''.join(word[0].upper() for word in re.findall(regex, words)) + return "".join(word[0].upper() for word in re.findall(regex, words)) diff --git a/evaluator/datasets/polyglot_py_unpatched/acronym/tests.py b/evaluator/datasets/polyglot_py_unpatched/acronym/tests.py index d289fcc0d..91135ca50 100644 --- a/evaluator/datasets/polyglot_py_unpatched/acronym/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/acronym/tests.py @@ -27,9 +27,7 @@ def test_punctuation_without_whitespace(self): def test_very_long_abbreviation(self): self.assertEqual( - abbreviate( - "Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me" - ), + abbreviate("Rolling On The Floor Laughing So Hard That My Dogs Came Over And Licked Me"), "ROTFLSHTMDCOALM", ) diff --git a/evaluator/datasets/polyglot_py_unpatched/affine-cipher/solution.py b/evaluator/datasets/polyglot_py_unpatched/affine-cipher/solution.py index 34ca0418d..11bb2126e 100644 --- a/evaluator/datasets/polyglot_py_unpatched/affine-cipher/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/affine-cipher/solution.py @@ -13,7 +13,7 @@ def mod_inverse(a_key, alphabet): def translate(text, a_key, b_key, mode): inverse = mod_inverse(a_key, ALPHABET) if inverse == 1: - raise ValueError('a and m must be coprime.') + raise ValueError("a and m must be coprime.") chars = [] for character in text: @@ -28,13 +28,12 @@ def translate(text, a_key, b_key, mode): new = (inverse * (origin - b_key)) % ALPHABET chars.append(chr(new + 97)) - return ''.join(chars) + return "".join(chars) def encode(plain, a, b): cipher = translate(plain, a, b, 0) - return ' '.join([cipher[idx:idx + BLOCK_SIZE] - for idx in range(0, len(cipher), BLOCK_SIZE)]) + return " ".join([cipher[idx : idx + BLOCK_SIZE] for idx in range(0, len(cipher), BLOCK_SIZE)]) def decode(ciphered, a, b): diff --git a/evaluator/datasets/polyglot_py_unpatched/affine-cipher/tests.py b/evaluator/datasets/polyglot_py_unpatched/affine-cipher/tests.py index 94a5fa622..b00094ac0 100644 --- a/evaluator/datasets/polyglot_py_unpatched/affine-cipher/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/affine-cipher/tests.py @@ -27,9 +27,7 @@ def test_encode_mindblowingly(self): self.assertEqual(encode("mindblowingly", 11, 15), "rzcwa gnxzc dgt") def test_encode_numbers(self): - self.assertEqual( - encode("Testing,1 2 3, testing.", 3, 4), "jqgjc rw123 jqgjc rw" - ) + self.assertEqual(encode("Testing,1 2 3, testing.", 3, 4), "jqgjc rw123 jqgjc rw") def test_encode_deep_thought(self): self.assertEqual(encode("Truth is fiction.", 5, 17), "iynia fdqfb ifje") @@ -71,9 +69,7 @@ def test_decode_with_no_spaces_in_input(self): ) def test_decode_with_too_many_spaces(self): - self.assertEqual( - decode("vszzm cly yd cg qdp", 15, 16), "jollygreengiant" - ) + self.assertEqual(decode("vszzm cly yd cg qdp", 15, 16), "jollygreengiant") def test_decode_with_a_not_coprime_to_m(self): with self.assertRaises(ValueError) as err: diff --git a/evaluator/datasets/polyglot_py_unpatched/all-your-base/solution.py b/evaluator/datasets/polyglot_py_unpatched/all-your-base/solution.py index e493a5024..097c96eb6 100644 --- a/evaluator/datasets/polyglot_py_unpatched/all-your-base/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/all-your-base/solution.py @@ -1,5 +1,5 @@ def from_digits(digits, base): - return sum(number * base ** idx for idx, number in enumerate(reversed(digits))) + return sum(number * base**idx for idx, number in enumerate(reversed(digits))) def to_digits(number, base_to): diff --git a/evaluator/datasets/polyglot_py_unpatched/all-your-base/tests.py b/evaluator/datasets/polyglot_py_unpatched/all-your-base/tests.py index f5cec438c..0beb52d63 100644 --- a/evaluator/datasets/polyglot_py_unpatched/all-your-base/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/all-your-base/tests.py @@ -68,17 +68,13 @@ def test_negative_digit(self): with self.assertRaises(ValueError) as err: rebase(2, [1, -1, 1, 0, 1, 0], 10) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "all digits must satisfy 0 <= d < input base" - ) + self.assertEqual(err.exception.args[0], "all digits must satisfy 0 <= d < input base") def test_invalid_positive_digit(self): with self.assertRaises(ValueError) as err: rebase(2, [1, 2, 1, 0, 1, 0], 10) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "all digits must satisfy 0 <= d < input base" - ) + self.assertEqual(err.exception.args[0], "all digits must satisfy 0 <= d < input base") def test_output_base_is_one(self): with self.assertRaises(ValueError) as err: diff --git a/evaluator/datasets/polyglot_py_unpatched/allergies/main.py b/evaluator/datasets/polyglot_py_unpatched/allergies/main.py index cd8a981a9..172449671 100644 --- a/evaluator/datasets/polyglot_py_unpatched/allergies/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/allergies/main.py @@ -1,5 +1,4 @@ class Allergies: - def __init__(self, score): pass diff --git a/evaluator/datasets/polyglot_py_unpatched/allergies/solution.py b/evaluator/datasets/polyglot_py_unpatched/allergies/solution.py index 44298c974..2e0352640 100644 --- a/evaluator/datasets/polyglot_py_unpatched/allergies/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/allergies/solution.py @@ -1,15 +1,5 @@ class Allergies: - - _allergies = [ - "eggs", - "peanuts", - "shellfish", - "strawberries", - "tomatoes", - "chocolate", - "pollen", - "cats" - ] + _allergies = ["eggs", "peanuts", "shellfish", "strawberries", "tomatoes", "chocolate", "pollen", "cats"] def __init__(self, score): self.score = score @@ -19,5 +9,4 @@ def allergic_to(self, item): @property def lst(self): - return [allergy for allergy in self._allergies - if self.allergic_to(allergy)] + return [allergy for allergy in self._allergies if self.allergic_to(allergy)] diff --git a/evaluator/datasets/polyglot_py_unpatched/alphametics/solution.py b/evaluator/datasets/polyglot_py_unpatched/alphametics/solution.py index 3faf17aaa..2001ec542 100644 --- a/evaluator/datasets/polyglot_py_unpatched/alphametics/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/alphametics/solution.py @@ -9,12 +9,12 @@ to reduce the number of permutations """ -from itertools import permutations, chain, product +from itertools import chain, permutations, product def dig_perms(digit_set, non_zero_chars, ok_zero_chars): """This function creates permutations given the set of digits, - letters not alllowed to be 0, and letters allowed to be 0 + letters not alllowed to be 0, and letters allowed to be 0 """ non_zero_count = len(non_zero_chars) # How many letters are non-0 ok_zero_count = len(ok_zero_chars) # How many letters are allowed 0 @@ -43,17 +43,20 @@ def dig_perms(digit_set, non_zero_chars, ok_zero_chars): # first iterator with all non-0 permutations # second iterator with all permulations without 1 letter # insert 0 in all possible positions of that permutation - return chain(permutations(non_zero_digit_set, total_count), - map(lambda iters: iters[0][:iters[1]] + (0,) + iters[0][iters[1]:], - product(permutations(non_zero_digit_set, total_count - 1), - positions_list))) + return chain( + permutations(non_zero_digit_set, total_count), + map( + lambda iters: iters[0][: iters[1]] + (0,) + iters[0][iters[1] :], + product(permutations(non_zero_digit_set, total_count - 1), positions_list), + ), + ) def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): """This function recursively traces a parsed expression from lowest - digits to highest, generating additional digits when necessary - checking the digit sum is divisible by 10, carrying the multiple of 10 - up to the next level + digits to highest, generating additional digits when necessary + checking the digit sum is divisible by 10, carrying the multiple of 10 + up to the next level """ # Basic parameters of the equation, # maximal digit rank @@ -93,8 +96,7 @@ def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): # build the dictionary for the new letters and this level new_dict = dict(zip(digit_letters, newdigs)) # complete the partial sum into test sum using the current permutation - testsum = part_sum + sum([new_dict[caesar] * van_gogh - for caesar, van_gogh in remaining_exp]) + testsum = part_sum + sum([new_dict[caesar] * van_gogh for caesar, van_gogh in remaining_exp]) # check if the sum is divisible by 10 dali, rembrandt = divmod(testsum, 10) if rembrandt == 0: @@ -103,9 +105,7 @@ def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): # proceed to the next level of recursion with # the same eqparams, but updated digit dictionary, # new carry over and remaining digits to assign - recurring_test = check_rec(eqparams, - (new_dict, dali, remaining_digits - set(newdigs)), - power + 1) + recurring_test = check_rec(eqparams, (new_dict, dali, remaining_digits - set(newdigs)), power + 1) # if the recursive call returned a non-empty dictionary # this means the recursion has found a solution # otherwise, proceed to the new permutation @@ -117,14 +117,15 @@ def check_rec(eqparams, trace_combo=({}, 0, set(range(10))), power=0): def solve(puzzle): - """A function to solve the alphametics problem - """ + """A function to solve the alphametics problem""" # First, split the expresion into left and right parts by == # split each part into words by + # strip spaces fro, each word, reverse each work to # enumerate the digit rank from lower to higer - full_exp = [list(map(lambda idx: list(reversed(idx.strip())), sigmund.split('+'))) - for sigmund in puzzle.strip().upper().split('==')] + full_exp = [ + list(map(lambda idx: list(reversed(idx.strip())), sigmund.split("+"))) + for sigmund in puzzle.strip().upper().split("==") + ] # Find the maximal lenght of the work, maximal possive digit rank or # the power of 10, should the < maxp max_digit_rank = max([len(warhol) for sigmund in full_exp for warhol in sigmund]) diff --git a/evaluator/datasets/polyglot_py_unpatched/anagram/solution.py b/evaluator/datasets/polyglot_py_unpatched/anagram/solution.py index 55929ff46..7b2d530f4 100644 --- a/evaluator/datasets/polyglot_py_unpatched/anagram/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/anagram/solution.py @@ -1,8 +1,10 @@ def find_anagrams(word, candidates): - return [candidate - for candidate in candidates - if _letters(candidate) == _letters(word) - if candidate.lower() != word.lower()] + return [ + candidate + for candidate in candidates + if _letters(candidate) == _letters(word) + if candidate.lower() != word.lower() + ] def _letters(word): diff --git a/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/solution.py b/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/solution.py index a358998e0..ad1af2ece 100644 --- a/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/solution.py @@ -1,18 +1,16 @@ from string import ascii_lowercase - BLOCK_SIZE = 5 trtbl = str.maketrans(ascii_lowercase, ascii_lowercase[::-1]) def base_trans(text): - return ''.join([character for character in text if character.isalnum()]).lower().translate(trtbl) + return "".join([character for character in text if character.isalnum()]).lower().translate(trtbl) def encode(plain): cipher = base_trans(plain) - return ' '.join(cipher[idx:idx + BLOCK_SIZE] - for idx in range(0, len(cipher), BLOCK_SIZE)) + return " ".join(cipher[idx : idx + BLOCK_SIZE] for idx in range(0, len(cipher), BLOCK_SIZE)) def decode(ciphered): diff --git a/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/tests.py b/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/tests.py index 313a27e00..cf629ed48 100644 --- a/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/atbash-cipher/tests.py @@ -60,6 +60,4 @@ def test_decode_with_too_many_spaces(self): self.assertEqual(decode("vc vix r hn"), "exercism") def test_decode_with_no_spaces(self): - self.assertEqual( - decode("zmlyhgzxovrhlugvmzhgvkkrmthglmv"), "anobstacleisoftenasteppingstone" - ) + self.assertEqual(decode("zmlyhgzxovrhlugvmzhgvkkrmthglmv"), "anobstacleisoftenasteppingstone") diff --git a/evaluator/datasets/polyglot_py_unpatched/bank-account/solution.py b/evaluator/datasets/polyglot_py_unpatched/bank-account/solution.py index 90ddf31c2..ecd41499b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/bank-account/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/bank-account/solution.py @@ -10,34 +10,34 @@ def __init__(self): def get_balance(self): with self.lock: if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") return self.balance def open(self): if self.is_open: - raise ValueError('account already open') + raise ValueError("account already open") self.is_open = True self.balance = 0 def deposit(self, amount): with self.lock: if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") if amount <= 0: - raise ValueError('amount must be greater than 0') + raise ValueError("amount must be greater than 0") self.balance += amount def withdraw(self, amount): with self.lock: if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") if amount <= 0: - raise ValueError('amount must be greater than 0') + raise ValueError("amount must be greater than 0") if amount > self.balance: - raise ValueError('amount must be less than balance') + raise ValueError("amount must be less than balance") self.balance -= amount def close(self): if not self.is_open: - raise ValueError('account not open') + raise ValueError("account not open") self.is_open = False diff --git a/evaluator/datasets/polyglot_py_unpatched/beer-song/solution.py b/evaluator/datasets/polyglot_py_unpatched/beer-song/solution.py index 4dfa8ea02..d36e419c3 100644 --- a/evaluator/datasets/polyglot_py_unpatched/beer-song/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/beer-song/solution.py @@ -3,35 +3,35 @@ def recite(start, take=1): for idx in range(start, start - take, -1): results.extend(verse(idx)) if idx > start - take + 1: - results.append('') + results.append("") return results def verse(number): return [ - f'{_bottles(number).capitalize()} of beer on the wall, {_bottles(number)} of beer.', - f'{_action(number)}{_next_bottle(number)}' + f"{_bottles(number).capitalize()} of beer on the wall, {_bottles(number)} of beer.", + f"{_action(number)}{_next_bottle(number)}", ] def _action(current_verse): if current_verse == 0: - return 'Go to the store and buy some more, ' + return "Go to the store and buy some more, " else: - return f'Take {"one" if current_verse > 1 else "it"} down and pass it around, ' + return f"Take {'one' if current_verse > 1 else 'it'} down and pass it around, " def _next_bottle(current_verse): - return f'{_bottles(_next_verse(current_verse))} of beer on the wall.' + return f"{_bottles(_next_verse(current_verse))} of beer on the wall." def _bottles(number): if number == 0: - return 'no more bottles' + return "no more bottles" if number == 1: - return '1 bottle' + return "1 bottle" else: - return f'{number} bottles' + return f"{number} bottles" def _next_verse(current_verse): diff --git a/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/main.py b/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/main.py index afca5d44e..59d731f64 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/main.py @@ -5,7 +5,7 @@ def __init__(self, data, left=None, right=None): self.right = None def __str__(self): - return f'TreeNode(data={self.data}, left={self.left}, right={self.right})' + return f"TreeNode(data={self.data}, left={self.left}, right={self.right})" class BinarySearchTree: diff --git a/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/solution.py b/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/solution.py index fede54dd6..ddcd494bc 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/solution.py @@ -5,7 +5,7 @@ def __init__(self, data, left=None, right=None): self.right = right def __str__(self): - return f'TreeNode(data={self.data}, left={self.left}, right={self.right})' + return f"TreeNode(data={self.data}, left={self.left}, right={self.right})" class BinarySearchTree: diff --git a/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/tests.py b/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/tests.py index 31a9d3bc1..8e631e806 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary-search-tree/tests.py @@ -33,9 +33,7 @@ def test_can_create_complex_tree(self): TreeNode("2", TreeNode("1", None, None), TreeNode("3", None, None)), TreeNode("6", TreeNode("5", None, None), TreeNode("7", None, None)), ) - self.assertTreeEqual( - BinarySearchTree(["4", "2", "6", "1", "3", "5", "7"]).data(), expected - ) + self.assertTreeEqual(BinarySearchTree(["4", "2", "6", "1", "3", "5", "7"]).data(), expected) def test_can_sort_single_number(self): expected = ["2"] @@ -55,9 +53,7 @@ def test_can_sort_if_second_number_is_greater_than_first(self): def test_can_sort_complex_tree(self): expected = ["1", "2", "3", "5", "6", "7"] - self.assertEqual( - BinarySearchTree(["2", "1", "3", "6", "7", "5"]).sorted_data(), expected - ) + self.assertEqual(BinarySearchTree(["2", "1", "3", "6", "7", "5"]).sorted_data(), expected) # Utilities def assertTreeEqual(self, tree_one, tree_two): diff --git a/evaluator/datasets/polyglot_py_unpatched/binary-search/solution.py b/evaluator/datasets/polyglot_py_unpatched/binary-search/solution.py index 0bd7be2cb..c5fd05879 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary-search/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary-search/solution.py @@ -9,4 +9,4 @@ def find(search_list, value): low = middle + 1 else: return middle - raise ValueError('value not in array') + raise ValueError("value not in array") diff --git a/evaluator/datasets/polyglot_py_unpatched/binary-search/tests.py b/evaluator/datasets/polyglot_py_unpatched/binary-search/tests.py index 5515c6abd..aa305b3bb 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary-search/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary-search/tests.py @@ -28,9 +28,7 @@ def test_finds_a_value_at_the_end_of_an_array(self): def test_finds_a_value_in_an_array_of_odd_length(self): - self.assertEqual( - find([1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144), 9 - ) + self.assertEqual(find([1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 634], 144), 9) def test_finds_a_value_in_an_array_of_even_length(self): diff --git a/evaluator/datasets/polyglot_py_unpatched/binary/solution.py b/evaluator/datasets/polyglot_py_unpatched/binary/solution.py index 364669062..77fd60814 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary/solution.py @@ -1,5 +1,4 @@ def parse_binary(digits): - if set(digits) - set('01'): - raise ValueError('Invalid binary literal: ' + digits) - return sum(int(digit) * 2 ** idx - for (idx, digit) in enumerate(reversed(digits))) + if set(digits) - set("01"): + raise ValueError("Invalid binary literal: " + digits) + return sum(int(digit) * 2**idx for (idx, digit) in enumerate(reversed(digits))) diff --git a/evaluator/datasets/polyglot_py_unpatched/binary/tests.py b/evaluator/datasets/polyglot_py_unpatched/binary/tests.py index 7186c94a6..140543329 100644 --- a/evaluator/datasets/polyglot_py_unpatched/binary/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/binary/tests.py @@ -4,6 +4,7 @@ If the argument to parse_binary isn't a valid binary number the function should raise a ValueError with a meaningful error message. """ + import unittest from main import parse_binary @@ -55,5 +56,6 @@ def test_invalid_binary_text_with_numbers(self): self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "Invalid binary literal: nope10") -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/bob/solution.py b/evaluator/datasets/polyglot_py_unpatched/bob/solution.py index 630ccccc6..5f9b0274a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/bob/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/bob/solution.py @@ -2,20 +2,20 @@ def response(hey_bob): hey_bob = hey_bob.strip() if _is_silence(hey_bob): - return 'Fine. Be that way!' + return "Fine. Be that way!" if _is_shouting(hey_bob): if _is_question(hey_bob): return "Calm down, I know what I'm doing!" else: - return 'Whoa, chill out!' + return "Whoa, chill out!" elif _is_question(hey_bob): - return 'Sure.' + return "Sure." else: - return 'Whatever.' + return "Whatever." def _is_silence(hey_bob): - return hey_bob == '' + return hey_bob == "" def _is_shouting(hey_bob): @@ -23,4 +23,4 @@ def _is_shouting(hey_bob): def _is_question(hey_bob): - return hey_bob.endswith('?') + return hey_bob.endswith("?") diff --git a/evaluator/datasets/polyglot_py_unpatched/bob/tests.py b/evaluator/datasets/polyglot_py_unpatched/bob/tests.py index 3fd8f4cff..70543b7fb 100644 --- a/evaluator/datasets/polyglot_py_unpatched/bob/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/bob/tests.py @@ -20,9 +20,7 @@ def test_shouting_gibberish(self): self.assertEqual(response("FCECDFCAAB"), "Whoa, chill out!") def test_asking_a_question(self): - self.assertEqual( - response("Does this cryogenic chamber make me look fat?"), "Sure." - ) + self.assertEqual(response("Does this cryogenic chamber make me look fat?"), "Sure.") def test_asking_a_numeric_question(self): self.assertEqual(response("You are, what, like 15?"), "Sure.") @@ -34,14 +32,10 @@ def test_talking_forcefully(self): self.assertEqual(response("Hi there!"), "Whatever.") def test_using_acronyms_in_regular_speech(self): - self.assertEqual( - response("It's OK if you don't want to go work for NASA."), "Whatever." - ) + self.assertEqual(response("It's OK if you don't want to go work for NASA."), "Whatever.") def test_forceful_question(self): - self.assertEqual( - response("WHAT'S GOING ON?"), "Calm down, I know what I'm doing!" - ) + self.assertEqual(response("WHAT'S GOING ON?"), "Calm down, I know what I'm doing!") def test_shouting_numbers(self): self.assertEqual(response("1, 2, 3 GO!"), "Whoa, chill out!") @@ -83,19 +77,13 @@ def test_starting_with_whitespace(self): self.assertEqual(response(" hmmmmmmm..."), "Whatever.") def test_ending_with_whitespace(self): - self.assertEqual( - response("Okay if like my spacebar quite a bit? "), "Sure." - ) + self.assertEqual(response("Okay if like my spacebar quite a bit? "), "Sure.") def test_other_whitespace(self): self.assertEqual(response("\n\r \t"), "Fine. Be that way!") def test_non_question_ending_with_whitespace(self): - self.assertEqual( - response("This is a statement ending with whitespace "), "Whatever." - ) + self.assertEqual(response("This is a statement ending with whitespace "), "Whatever.") def test_multiple_line_question(self): - self.assertEqual( - response("\nDoes this cryogenic chamber make\n me look fat?"), "Sure." - ) + self.assertEqual(response("\nDoes this cryogenic chamber make\n me look fat?"), "Sure.") diff --git a/evaluator/datasets/polyglot_py_unpatched/bottle-song/solution.py b/evaluator/datasets/polyglot_py_unpatched/bottle-song/solution.py index 18e1167da..7f4bfaa3a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/bottle-song/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/bottle-song/solution.py @@ -1,34 +1,46 @@ -NUMBERS = {10: "ten", 9: "nine", 8: "eight", 7: "seven", 6: "six", 5: "five", 4: "four", 3: "three", 2: "two", 1: "one", 0: "no"} +NUMBERS = { + 10: "ten", + 9: "nine", + 8: "eight", + 7: "seven", + 6: "six", + 5: "five", + 4: "four", + 3: "three", + 2: "two", + 1: "one", + 0: "no", +} + def recite(start, take=1): results = [] for idx in range(start, start - take, -1): results.extend(verse(idx)) if idx > start - take + 1: - results.append('') + results.append("") return results def verse(number): - return [ - *main_verse(number), - "And if one green bottle should accidentally fall,", - last_verse(number) - ] + return [*main_verse(number), "And if one green bottle should accidentally fall,", last_verse(number)] + def main_verse(number): if number == 1: return [ - f'One green bottle hanging on the wall,', - f'One green bottle hanging on the wall,', + "One green bottle hanging on the wall,", + "One green bottle hanging on the wall,", ] else: return [ - f'{NUMBERS[number].capitalize()} green bottles hanging on the wall,', - f'{NUMBERS[number].capitalize()} green bottles hanging on the wall,',] + f"{NUMBERS[number].capitalize()} green bottles hanging on the wall,", + f"{NUMBERS[number].capitalize()} green bottles hanging on the wall,", + ] + def last_verse(number): - if number -1 == 1: - return f"There'll be one green bottle hanging on the wall." + if number - 1 == 1: + return "There'll be one green bottle hanging on the wall." else: - return f"There'll be {NUMBERS[number-1]} green bottles hanging on the wall." + return f"There'll be {NUMBERS[number - 1]} green bottles hanging on the wall." diff --git a/evaluator/datasets/polyglot_py_unpatched/bowling/solution.py b/evaluator/datasets/polyglot_py_unpatched/bowling/solution.py index 7afb0d63f..7fe4d82e8 100644 --- a/evaluator/datasets/polyglot_py_unpatched/bowling/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/bowling/solution.py @@ -59,26 +59,23 @@ def next_throws(self, frame_idx): def roll_bonus(self, pins): tenth_frame = self.frames[-1] if tenth_frame.is_open(): - raise IndexError('cannot throw bonus with an open tenth frame') + raise IndexError("cannot throw bonus with an open tenth frame") self.bonus_throws.append(pins) # Check against invalid fill balls, e.g. [3, 10] - if (len(self.bonus_throws) == 2 and self.bonus_throws[0] != 10 and - sum(self.bonus_throws) > 10): - raise ValueError('invalid fill balls') + if len(self.bonus_throws) == 2 and self.bonus_throws[0] != 10 and sum(self.bonus_throws) > 10: + raise ValueError("invalid fill balls") # Check if there are more bonuses than it should be if tenth_frame.is_strike() and len(self.bonus_throws) > 2: - raise IndexError( - 'wrong number of fill balls when the tenth frame is a strike') + raise IndexError("wrong number of fill balls when the tenth frame is a strike") elif tenth_frame.is_spare() and len(self.bonus_throws) > 1: - raise IndexError( - 'wrong number of fill balls when the tenth frame is a spare') + raise IndexError("wrong number of fill balls when the tenth frame is a spare") def roll(self, pins): if not 0 <= pins <= 10: - raise ValueError('invalid pins') + raise ValueError("invalid pins") elif self.current_frame_idx == MAX_FRAME: self.roll_bonus(pins) else: @@ -88,12 +85,9 @@ def roll(self, pins): def score(self): if self.current_frame_idx < MAX_FRAME: - raise IndexError('frame less than 10') + raise IndexError("frame less than 10") if self.frames[-1].is_spare() and len(self.bonus_throws) != 1: - raise IndexError( - 'one bonus must be rolled when the tenth frame is spare') + raise IndexError("one bonus must be rolled when the tenth frame is spare") if self.frames[-1].is_strike() and len(self.bonus_throws) != 2: - raise IndexError( - 'two bonuses must be rolled when the tenth frame is strike') - return sum(frame.score(self.next_throws(frame.idx)) - for frame in self.frames) + raise IndexError("two bonuses must be rolled when the tenth frame is strike") + return sum(frame.score(self.next_throws(frame.idx)) for frame in self.frames) diff --git a/evaluator/datasets/polyglot_py_unpatched/change/solution.py b/evaluator/datasets/polyglot_py_unpatched/change/solution.py index b62fa0cc1..03c562643 100644 --- a/evaluator/datasets/polyglot_py_unpatched/change/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/change/solution.py @@ -20,6 +20,6 @@ def find_fewest_coins(coins, target): last_coin_value = target array = [] while last_coin[last_coin_value] != -1: - array.append(last_coin_value-last_coin[last_coin_value]) + array.append(last_coin_value - last_coin[last_coin_value]) last_coin_value = last_coin[last_coin_value] return array diff --git a/evaluator/datasets/polyglot_py_unpatched/circular-buffer/main.py b/evaluator/datasets/polyglot_py_unpatched/circular-buffer/main.py index 87583f62c..ad2fb4a67 100644 --- a/evaluator/datasets/polyglot_py_unpatched/circular-buffer/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/circular-buffer/main.py @@ -4,6 +4,7 @@ class BufferFullException(BufferError): message: explanation of the error. """ + def __init__(self, message): pass @@ -14,6 +15,7 @@ class BufferEmptyException(BufferError): message: explanation of the error. """ + def __init__(self, message): pass diff --git a/evaluator/datasets/polyglot_py_unpatched/circular-buffer/solution.py b/evaluator/datasets/polyglot_py_unpatched/circular-buffer/solution.py index 538cc7bc5..bce894327 100644 --- a/evaluator/datasets/polyglot_py_unpatched/circular-buffer/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/circular-buffer/solution.py @@ -4,6 +4,7 @@ class BufferFullException(BufferError): message: explanation of the error. """ + def __init__(self, message): self.message = message @@ -14,12 +15,12 @@ class BufferEmptyException(BufferError): message: explanation of the error. """ + def __init__(self, message): self.message = message class CircularBuffer: - def __init__(self, capacity): self.buffer = bytearray(capacity) self.read_point = 0 @@ -37,7 +38,7 @@ def clear(self): def write(self, data): if all(self.buffer): - raise BufferFullException('Circular buffer is full') + raise BufferFullException("Circular buffer is full") self._update_buffer(data) self.write_point = (self.write_point + 1) % len(self.buffer) @@ -49,7 +50,7 @@ def overwrite(self, data): def read(self): if not any(self.buffer): - raise BufferEmptyException('Circular buffer is empty') + raise BufferEmptyException("Circular buffer is empty") data = chr(self.buffer[self.read_point]) self.buffer[self.read_point] = 0 self.read_point = (self.read_point + 1) % len(self.buffer) diff --git a/evaluator/datasets/polyglot_py_unpatched/circular-buffer/tests.py b/evaluator/datasets/polyglot_py_unpatched/circular-buffer/tests.py index d91dbc7f7..0458733bf 100644 --- a/evaluator/datasets/polyglot_py_unpatched/circular-buffer/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/circular-buffer/tests.py @@ -5,9 +5,9 @@ import unittest from main import ( - CircularBuffer, BufferEmptyException, BufferFullException, + CircularBuffer, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/clock/solution.py b/evaluator/datasets/polyglot_py_unpatched/clock/solution.py index 47d526816..25ddfc1af 100644 --- a/evaluator/datasets/polyglot_py_unpatched/clock/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/clock/solution.py @@ -7,10 +7,10 @@ def __init__(self, hour, minute): self.cleanup() def __repr__(self): - return f'Clock({self.hour}, {self.minute})' + return f"Clock({self.hour}, {self.minute})" def __str__(self): - return '{:02d}:{:02d}'.format(self.hour, self.minute) + return "{:02d}:{:02d}".format(self.hour, self.minute) def __eq__(self, other): return repr(self) == repr(other) diff --git a/evaluator/datasets/polyglot_py_unpatched/collatz-conjecture/solution.py b/evaluator/datasets/polyglot_py_unpatched/collatz-conjecture/solution.py index 843fe43e6..2ddcc546a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/collatz-conjecture/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/collatz-conjecture/solution.py @@ -1,6 +1,6 @@ def steps(number): if number <= 0: - raise ValueError('Only positive integers are allowed') + raise ValueError("Only positive integers are allowed") step_count = 0 while number > 1: diff --git a/evaluator/datasets/polyglot_py_unpatched/complex-numbers/solution.py b/evaluator/datasets/polyglot_py_unpatched/complex-numbers/solution.py index b833c38fc..5141c5d25 100644 --- a/evaluator/datasets/polyglot_py_unpatched/complex-numbers/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/complex-numbers/solution.py @@ -1,5 +1,6 @@ import math + class ComplexNumber: def __init__(self, real=0, imaginary=0): self.real = real diff --git a/evaluator/datasets/polyglot_py_unpatched/complex-numbers/tests.py b/evaluator/datasets/polyglot_py_unpatched/complex-numbers/tests.py index 61ec78c51..417fab8fd 100644 --- a/evaluator/datasets/polyglot_py_unpatched/complex-numbers/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/complex-numbers/tests.py @@ -11,7 +11,6 @@ class ComplexNumbersTest(unittest.TestCase): - # Real part def test_real_part_of_a_purely_real_number(self): @@ -35,9 +34,7 @@ def test_imaginary_part_of_a_number_with_real_and_imaginary_part(self): self.assertEqual(ComplexNumber(1, 2).imaginary, 2) def test_imaginary_unit(self): - self.assertEqual( - ComplexNumber(0, 1) * ComplexNumber(0, 1), ComplexNumber(-1, 0) - ) + self.assertEqual(ComplexNumber(0, 1) * ComplexNumber(0, 1), ComplexNumber(-1, 0)) # Arithmetic @@ -55,19 +52,13 @@ def test_add_numbers_with_real_and_imaginary_part(self): # Subtraction def test_subtract_purely_real_numbers(self): - self.assertEqual( - ComplexNumber(1, 0) - ComplexNumber(2, 0), ComplexNumber(-1, 0) - ) + self.assertEqual(ComplexNumber(1, 0) - ComplexNumber(2, 0), ComplexNumber(-1, 0)) def test_subtract_purely_imaginary_numbers(self): - self.assertEqual( - ComplexNumber(0, 1) - ComplexNumber(0, 2), ComplexNumber(0, -1) - ) + self.assertEqual(ComplexNumber(0, 1) - ComplexNumber(0, 2), ComplexNumber(0, -1)) def test_subtract_numbers_with_real_and_imaginary_part(self): - self.assertEqual( - ComplexNumber(1, 2) - ComplexNumber(3, 4), ComplexNumber(-2, -2) - ) + self.assertEqual(ComplexNumber(1, 2) - ComplexNumber(3, 4), ComplexNumber(-2, -2)) # Multiplication @@ -75,31 +66,21 @@ def test_multiply_purely_real_numbers(self): self.assertEqual(ComplexNumber(1, 0) * ComplexNumber(2, 0), ComplexNumber(2, 0)) def test_multiply_purely_imaginary_numbers(self): - self.assertEqual( - ComplexNumber(0, 1) * ComplexNumber(0, 2), ComplexNumber(-2, 0) - ) + self.assertEqual(ComplexNumber(0, 1) * ComplexNumber(0, 2), ComplexNumber(-2, 0)) def test_multiply_numbers_with_real_and_imaginary_part(self): - self.assertEqual( - ComplexNumber(1, 2) * ComplexNumber(3, 4), ComplexNumber(-5, 10) - ) + self.assertEqual(ComplexNumber(1, 2) * ComplexNumber(3, 4), ComplexNumber(-5, 10)) # Division def test_divide_purely_real_numbers(self): - self.assertAlmostEqual( - ComplexNumber(1, 0) / ComplexNumber(2, 0), ComplexNumber(0.5, 0) - ) + self.assertAlmostEqual(ComplexNumber(1, 0) / ComplexNumber(2, 0), ComplexNumber(0.5, 0)) def test_divide_purely_imaginary_numbers(self): - self.assertAlmostEqual( - ComplexNumber(0, 1) / ComplexNumber(0, 2), ComplexNumber(0.5, 0) - ) + self.assertAlmostEqual(ComplexNumber(0, 1) / ComplexNumber(0, 2), ComplexNumber(0.5, 0)) def test_divide_numbers_with_real_and_imaginary_part(self): - self.assertAlmostEqual( - ComplexNumber(1, 2) / ComplexNumber(3, 4), ComplexNumber(0.44, 0.08) - ) + self.assertAlmostEqual(ComplexNumber(1, 2) / ComplexNumber(3, 4), ComplexNumber(0.44, 0.08)) # Absolute value @@ -145,14 +126,10 @@ def test_exponential_of_a_purely_real_number(self): self.assertAlmostEqual(ComplexNumber(1, 0).exp(), ComplexNumber(math.e, 0)) def test_exponential_of_a_number_with_real_and_imaginary_part(self): - self.assertAlmostEqual( - ComplexNumber(math.log(2), math.pi).exp(), ComplexNumber(-2, 0) - ) + self.assertAlmostEqual(ComplexNumber(math.log(2), math.pi).exp(), ComplexNumber(-2, 0)) def test_exponential_resulting_in_a_number_with_real_and_imaginary_part(self): - self.assertAlmostEqual( - ComplexNumber(math.log(2) / 2, math.pi / 4).exp(), ComplexNumber(1, 1) - ) + self.assertAlmostEqual(ComplexNumber(math.log(2) / 2, math.pi / 4).exp(), ComplexNumber(1, 1)) # Operations between real numbers and complex numbers diff --git a/evaluator/datasets/polyglot_py_unpatched/connect/main.py b/evaluator/datasets/polyglot_py_unpatched/connect/main.py index 688dd688a..f10f9a4de 100644 --- a/evaluator/datasets/polyglot_py_unpatched/connect/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/connect/main.py @@ -1,4 +1,3 @@ - class ConnectGame: def __init__(self, board): pass diff --git a/evaluator/datasets/polyglot_py_unpatched/connect/solution.py b/evaluator/datasets/polyglot_py_unpatched/connect/solution.py index 0cb003348..412e42395 100644 --- a/evaluator/datasets/polyglot_py_unpatched/connect/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/connect/solution.py @@ -1,9 +1,7 @@ - class ConnectGame: - DIRECTIONS = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, -1), (-1, 1)] - WHITE = 'O' - BLACK = 'X' + WHITE = "O" + BLACK = "X" def __init__(self, lines): self.board = ConnectGame.make_board(lines) @@ -21,7 +19,7 @@ def valid(self, width, height): @staticmethod def make_board(lines): - return [''.join(cur_line.split()) for cur_line in lines.splitlines()] + return ["".join(cur_line.split()) for cur_line in lines.splitlines()] def player_reach_dest(self, player, width, height): if player == self.BLACK: @@ -63,4 +61,4 @@ def get_winner(self): return self.BLACK if self.check_player_is_winner(self.WHITE): return self.WHITE - return '' + return "" diff --git a/evaluator/datasets/polyglot_py_unpatched/crypto-square/solution.py b/evaluator/datasets/polyglot_py_unpatched/crypto-square/solution.py index 8cfc7642f..3b1256797 100644 --- a/evaluator/datasets/polyglot_py_unpatched/crypto-square/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/crypto-square/solution.py @@ -1,20 +1,17 @@ -from math import ceil, sqrt from itertools import zip_longest +from math import ceil, sqrt def cipher_text(plain_text): plain_text = _cleanse(plain_text) square_size = int(ceil(sqrt(len(plain_text)))) square = _chunks_of(plain_text, square_size) - return ' '.join([''.join(column) - for column in zip_longest(*square, fillvalue=' ')]) + return " ".join(["".join(column) for column in zip_longest(*square, fillvalue=" ")]) def _cleanse(text): - """Lowercase a string and remove punctuation and whitespace - """ - return ''.join([character for character in text - if character.isalnum()]).lower() + """Lowercase a string and remove punctuation and whitespace""" + return "".join([character for character in text if character.isalnum()]).lower() def _chunks_of(text, num): diff --git a/evaluator/datasets/polyglot_py_unpatched/darts/solution.py b/evaluator/datasets/polyglot_py_unpatched/darts/solution.py index eed150cea..7086b8dd6 100644 --- a/evaluator/datasets/polyglot_py_unpatched/darts/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/darts/solution.py @@ -1,5 +1,6 @@ from math import sqrt + # X, and Y variable names against [pylint]: C0104, but is the same as the stub, advise not to change this. def score(x, y): dart_location = sqrt(x * x + y * y) diff --git a/evaluator/datasets/polyglot_py_unpatched/diamond/solution.py b/evaluator/datasets/polyglot_py_unpatched/diamond/solution.py index bd9924764..ad9014f94 100644 --- a/evaluator/datasets/polyglot_py_unpatched/diamond/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/diamond/solution.py @@ -8,8 +8,8 @@ def rows(letter): def make_half(lines, columns): diamond_half = [] for number in range(lines): - row = [' '] * columns + row = [" "] * columns row[lines - 1 - number] = chr(number + 65) row[lines - 1 + number] = chr(number + 65) - diamond_half.append(''.join(row)) + diamond_half.append("".join(row)) return diamond_half diff --git a/evaluator/datasets/polyglot_py_unpatched/diffie-hellman/solution.py b/evaluator/datasets/polyglot_py_unpatched/diffie-hellman/solution.py index 45a547bcb..d4937e1f7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/diffie-hellman/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/diffie-hellman/solution.py @@ -2,7 +2,7 @@ def private_key(p): - return random.randint(2, p-1) + return random.randint(2, p - 1) def public_key(p, g, private): diff --git a/evaluator/datasets/polyglot_py_unpatched/dnd-character/main.py b/evaluator/datasets/polyglot_py_unpatched/dnd-character/main.py index 1d310dde8..acfe6aef0 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dnd-character/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/dnd-character/main.py @@ -2,5 +2,6 @@ class Character: def __init__(self): pass + def modifier(value): pass diff --git a/evaluator/datasets/polyglot_py_unpatched/dnd-character/solution.py b/evaluator/datasets/polyglot_py_unpatched/dnd-character/solution.py index fad3dfe17..175e134d3 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dnd-character/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/dnd-character/solution.py @@ -1,5 +1,5 @@ -import random import math +import random class Character: @@ -18,4 +18,4 @@ def ability(self): def modifier(value): - return math.floor((value-10)/2) + return math.floor((value - 10) / 2) diff --git a/evaluator/datasets/polyglot_py_unpatched/dominoes/solution.py b/evaluator/datasets/polyglot_py_unpatched/dominoes/solution.py index 2490525ad..604b851a5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dominoes/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/dominoes/solution.py @@ -1,5 +1,5 @@ -from itertools import permutations from functools import reduce +from itertools import permutations def swap(item_1, item_2): diff --git a/evaluator/datasets/polyglot_py_unpatched/dominoes/tests.py b/evaluator/datasets/polyglot_py_unpatched/dominoes/tests.py index 08eefbcbf..aa6a4cb63 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dominoes/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/dominoes/tests.py @@ -91,30 +91,19 @@ def normalize_dominoes(self, dominoes): return list(sorted(tuple(sorted(domino)) for domino in dominoes)) def assert_same_dominoes(self, input_dominoes, output_chain): - msg = ( - "Dominoes used in the output must be the same " - "as the ones given in the input" - ) + msg = "Dominoes used in the output must be the same as the ones given in the input" input_normal = self.normalize_dominoes(input_dominoes) output_normal = self.normalize_dominoes(output_chain) self.assertEqual(input_normal, output_normal, msg) def assert_consecutive_dominoes_match(self, output_chain): for i in range(len(output_chain) - 1): - msg = ( - "In chain {}, right end of domino {} ({}) " - "and left end of domino {} ({}) must match" - ) - msg = msg.format( - output_chain, i, output_chain[i], i + 1, output_chain[i + 1] - ) + msg = "In chain {}, right end of domino {} ({}) and left end of domino {} ({}) must match" + msg = msg.format(output_chain, i, output_chain[i], i + 1, output_chain[i + 1]) self.assertEqual(output_chain[i][1], output_chain[i + 1][0], msg) def assert_dominoes_at_ends_match(self, output_chain): - msg = ( - "In chain {}, left end of first domino ({}) and " - "right end of last domino ({}) must match" - ) + msg = "In chain {}, left end of first domino ({}) and right end of last domino ({}) must match" msg = msg.format(output_chain, output_chain[0], output_chain[-1]) self.assertEqual(output_chain[0][0], output_chain[-1][1], msg) diff --git a/evaluator/datasets/polyglot_py_unpatched/dot-dsl/main.py b/evaluator/datasets/polyglot_py_unpatched/dot-dsl/main.py index e22a618bd..d1de6211a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dot-dsl/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/dot-dsl/main.py @@ -17,9 +17,7 @@ def __init__(self, src, dst, attrs): self.attrs = attrs def __eq__(self, other): - return (self.src == other.src and - self.dst == other.dst and - self.attrs == other.attrs) + return self.src == other.src and self.dst == other.dst and self.attrs == other.attrs class Graph: diff --git a/evaluator/datasets/polyglot_py_unpatched/dot-dsl/solution.py b/evaluator/datasets/polyglot_py_unpatched/dot-dsl/solution.py index 9229cc410..3810ac7c1 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dot-dsl/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/dot-dsl/solution.py @@ -17,9 +17,7 @@ def __init__(self, src, dst, attrs): self.attrs = attrs def __eq__(self, other): - return (self.src == other.src and - self.dst == other.dst and - self.attrs == other.attrs) + return self.src == other.src and self.dst == other.dst and self.attrs == other.attrs class Graph: @@ -32,24 +30,24 @@ def __init__(self, data=None): data = [] if not isinstance(data, list): - raise TypeError('Graph data malformed') + raise TypeError("Graph data malformed") for item in data: if len(item) < 3: - raise TypeError('Graph item incomplete') + raise TypeError("Graph item incomplete") type_ = item[0] if type_ == ATTR: if len(item) != 3: - raise ValueError('Attribute is malformed') + raise ValueError("Attribute is malformed") self.attrs[item[1]] = item[2] elif type_ == NODE: if len(item) != 3: - raise ValueError('Node is malformed') + raise ValueError("Node is malformed") self.nodes.append(Node(item[1], item[2])) elif type_ == EDGE: if len(item) != 4: - raise ValueError('Edge is malformed') + raise ValueError("Edge is malformed") self.edges.append(Edge(item[1], item[2], item[3])) else: - raise ValueError('Unknown item') + raise ValueError("Unknown item") diff --git a/evaluator/datasets/polyglot_py_unpatched/dot-dsl/tests.py b/evaluator/datasets/polyglot_py_unpatched/dot-dsl/tests.py index b4cdbf4e4..4ed18fdc6 100644 --- a/evaluator/datasets/polyglot_py_unpatched/dot-dsl/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/dot-dsl/tests.py @@ -1,6 +1,6 @@ import unittest -from main import Graph, Node, Edge, NODE, EDGE, ATTR +from main import ATTR, EDGE, NODE, Edge, Graph, Node class DotDslTest(unittest.TestCase): @@ -12,63 +12,50 @@ def test_empty_graph(self): self.assertEqual(g.attrs, {}) def test_graph_with_one_node(self): - g = Graph([ - (NODE, "a", {}) - ]) + g = Graph([(NODE, "a", {})]) self.assertEqual(g.nodes, [Node("a", {})]) self.assertEqual(g.edges, []) self.assertEqual(g.attrs, {}) def test_graph_with_one_node_with_keywords(self): - g = Graph([ - (NODE, "a", {"color": "green"}) - ]) + g = Graph([(NODE, "a", {"color": "green"})]) self.assertEqual(g.nodes, [Node("a", {"color": "green"})]) self.assertEqual(g.edges, []) self.assertEqual(g.attrs, {}) def test_graph_with_one_edge(self): - g = Graph([ - (EDGE, "a", "b", {}) - ]) + g = Graph([(EDGE, "a", "b", {})]) self.assertEqual(g.nodes, []) self.assertEqual(g.edges, [Edge("a", "b", {})]) self.assertEqual(g.attrs, {}) def test_graph_with_one_attribute(self): - g = Graph([ - (ATTR, "foo", "1") - ]) + g = Graph([(ATTR, "foo", "1")]) self.assertEqual(g.nodes, []) self.assertEqual(g.edges, []) self.assertEqual(g.attrs, {"foo": "1"}) def test_graph_with_attributes(self): - g = Graph([ - (ATTR, "foo", "1"), - (ATTR, "title", "Testing Attrs"), - (NODE, "a", {"color": "green"}), - (NODE, "c", {}), - (NODE, "b", {"label": "Beta!"}), - (EDGE, "b", "c", {}), - (EDGE, "a", "b", {"color": "blue"}), - (ATTR, "bar", "true") - ]) - - self.assertEqual(g.nodes, [Node("a", {"color": "green"}), - Node("c", {}), - Node("b", {"label": "Beta!"})]) - self.assertEqual(g.edges, [Edge("b", "c", {}), - Edge("a", "b", {"color": "blue"})]) - self.assertEqual(g.attrs, { - "foo": "1", - "title": "Testing Attrs", - "bar": "true" - }) + g = Graph( + [ + (ATTR, "foo", "1"), + (ATTR, "title", "Testing Attrs"), + (NODE, "a", {"color": "green"}), + (NODE, "c", {}), + (NODE, "b", {"label": "Beta!"}), + (EDGE, "b", "c", {}), + (EDGE, "a", "b", {"color": "blue"}), + (ATTR, "bar", "true"), + ] + ) + + self.assertEqual(g.nodes, [Node("a", {"color": "green"}), Node("c", {}), Node("b", {"label": "Beta!"})]) + self.assertEqual(g.edges, [Edge("b", "c", {}), Edge("a", "b", {"color": "blue"})]) + self.assertEqual(g.attrs, {"foo": "1", "title": "Testing Attrs", "bar": "true"}) def test_malformed_graph(self): with self.assertRaises(TypeError) as err: @@ -87,27 +74,23 @@ def test_malformed_graph_item(self): self.assertEqual(type(err.exception), TypeError) self.assertEqual(err.exception.args[0], "Graph item incomplete") - with self.assertRaises(TypeError) as err: - Graph([(ATTR, )]) + Graph([(ATTR,)]) self.assertEqual(type(err.exception), TypeError) self.assertEqual(err.exception.args[0], "Graph item incomplete") - def test_malformed_attr(self): with self.assertRaises(ValueError) as err: Graph([(ATTR, 1, 2, 3)]) self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "Attribute is malformed") - def test_malformed_node(self): with self.assertRaises(ValueError) as err: Graph([(NODE, 1, 2, 3)]) self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "Node is malformed") - def test_malformed_EDGE(self): with self.assertRaises(ValueError) as err: Graph([(EDGE, 1, 2)]) diff --git a/evaluator/datasets/polyglot_py_unpatched/error-handling/solution.py b/evaluator/datasets/polyglot_py_unpatched/error-handling/solution.py index 1dba190d4..f4b7fd9a2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/error-handling/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/error-handling/solution.py @@ -1,5 +1,5 @@ def handle_error_by_throwing_exception(): - raise Exception('Meaningful message describing the source of the error') + raise Exception("Meaningful message describing the source of the error") def handle_error_by_returning_none(input_data): diff --git a/evaluator/datasets/polyglot_py_unpatched/error-handling/tests.py b/evaluator/datasets/polyglot_py_unpatched/error-handling/tests.py index 0a1635349..2c73e2272 100644 --- a/evaluator/datasets/polyglot_py_unpatched/error-handling/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/error-handling/tests.py @@ -2,6 +2,7 @@ import main as er + class FileLike: def __init__(self, fail_something=True): self.is_open = False @@ -29,52 +30,43 @@ def do_something(self): if self.fail_something: raise Exception("Failed while doing something") + class ErrorHandlingTest(unittest.TestCase): def test_throw_exception(self): with self.assertRaisesWithMessage(Exception): er.handle_error_by_throwing_exception() def test_return_none(self): - self.assertEqual(er.handle_error_by_returning_none('1'), 1, - 'Result of valid input should not be None') - self.assertIsNone(er.handle_error_by_returning_none('a'), - 'Result of invalid input should be None') + self.assertEqual(er.handle_error_by_returning_none("1"), 1, "Result of valid input should not be None") + self.assertIsNone(er.handle_error_by_returning_none("a"), "Result of invalid input should be None") def test_return_tuple(self): - successful_result, result = er.handle_error_by_returning_tuple('1') - self.assertIs(successful_result, True, - 'Valid input should be successful') - self.assertEqual(result, 1, 'Result of valid input should not be None') + successful_result, result = er.handle_error_by_returning_tuple("1") + self.assertIs(successful_result, True, "Valid input should be successful") + self.assertEqual(result, 1, "Result of valid input should not be None") - failure_result, result = er.handle_error_by_returning_tuple('a') - self.assertIs(failure_result, False, - 'Invalid input should not be successful') + failure_result, result = er.handle_error_by_returning_tuple("a") + self.assertIs(failure_result, False, "Invalid input should not be successful") def test_filelike_objects_are_closed_on_exception(self): filelike_object = FileLike(fail_something=True) with self.assertRaisesWithMessage(Exception): er.filelike_objects_are_closed_on_exception(filelike_object) - self.assertIs(filelike_object.is_open, False, - 'filelike_object should be closed') - self.assertIs(filelike_object.was_open, True, - 'filelike_object should have been opened') - self.assertIs(filelike_object.did_something, True, - 'filelike_object should call do_something()') + self.assertIs(filelike_object.is_open, False, "filelike_object should be closed") + self.assertIs(filelike_object.was_open, True, "filelike_object should have been opened") + self.assertIs(filelike_object.did_something, True, "filelike_object should call do_something()") def test_filelike_objects_are_closed_without_exception(self): filelike_object = FileLike(fail_something=False) er.filelike_objects_are_closed_on_exception(filelike_object) - self.assertIs(filelike_object.is_open, False, - 'filelike_object should be closed') - self.assertIs(filelike_object.was_open, True, - 'filelike_object should have been opened') - self.assertIs(filelike_object.did_something, True, - 'filelike_object should call do_something()') + self.assertIs(filelike_object.is_open, False, "filelike_object should be closed") + self.assertIs(filelike_object.was_open, True, "filelike_object should have been opened") + self.assertIs(filelike_object.did_something, True, "filelike_object should call do_something()") # Utility functions def assertRaisesWithMessage(self, exception): return self.assertRaisesRegex(exception, r".+") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/etl/solution.py b/evaluator/datasets/polyglot_py_unpatched/etl/solution.py index 0e2b65824..4c9df1da8 100644 --- a/evaluator/datasets/polyglot_py_unpatched/etl/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/etl/solution.py @@ -1,6 +1,2 @@ def transform(legacy_data): - return { - letter.lower(): points - for points, letters in legacy_data.items() - for letter in letters - } + return {letter.lower(): points for points, letters in legacy_data.items() for letter in letters} diff --git a/evaluator/datasets/polyglot_py_unpatched/flower-field/solution.py b/evaluator/datasets/polyglot_py_unpatched/flower-field/solution.py index e3ae009cc..ed550d208 100644 --- a/evaluator/datasets/polyglot_py_unpatched/flower-field/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/flower-field/solution.py @@ -8,33 +8,33 @@ def annotate(garden): for index1 in range(col_len): for index2 in range(row_len): - if board[index1][index2] != ' ': + if board[index1][index2] != " ": continue low = max(index2 - 1, 0) high = min(index2 + 2, row_len + 2) - counts = garden[index1][low:high].count('*') + counts = garden[index1][low:high].count("*") if index1 > 0: - counts += garden[index1 - 1][low:high].count('*') + counts += garden[index1 - 1][low:high].count("*") if index1 < col_len - 1: - counts += garden[index1 + 1][low:high].count('*') + counts += garden[index1 + 1][low:high].count("*") if counts == 0: continue board[index1][index2] = str(counts) - return [''.join(row) for row in board] + return ["".join(row) for row in board] def verify_board(garden): # Rows with different lengths row_len = len(garden[0]) if not all(len(row) == row_len for row in garden): - raise ValueError('The board is invalid with current input.') + raise ValueError("The board is invalid with current input.") # Unknown character in board character_set = set() for row in garden: character_set.update(row) - if character_set - set(' *'): - raise ValueError('The board is invalid with current input.') + if character_set - set(" *"): + raise ValueError("The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py_unpatched/flower-field/tests.py b/evaluator/datasets/polyglot_py_unpatched/flower-field/tests.py index f59493969..12df7e013 100644 --- a/evaluator/datasets/polyglot_py_unpatched/flower-field/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/flower-field/tests.py @@ -63,14 +63,10 @@ def test_different_len(self): with self.assertRaises(ValueError) as err: annotate([" ", "* ", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") def test_invalid_char(self): with self.assertRaises(ValueError) as err: annotate(["X * "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py_unpatched/food-chain/solution.py b/evaluator/datasets/polyglot_py_unpatched/food-chain/solution.py index 075f83faf..6c2e486f7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/food-chain/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/food-chain/solution.py @@ -1,62 +1,64 @@ def get_song(): - animals = ['fly', 'spider', 'bird', 'cat', 'dog', 'goat', 'cow', 'horse'] - - phrases = [' wriggled and jiggled and tickled inside her.', - 'How absurd to swallow a bird!', - 'Imagine that, to swallow a cat!', - 'What a hog, to swallow a dog!', - 'Just opened her throat and swallowed a goat!', - "I don't know how she swallowed a cow!", - "She's dead, of course!"] - - old_lady = 'I know an old lady who swallowed a ' - swallowed = 'She swallowed the to catch the ' + animals = ["fly", "spider", "bird", "cat", "dog", "goat", "cow", "horse"] + + phrases = [ + " wriggled and jiggled and tickled inside her.", + "How absurd to swallow a bird!", + "Imagine that, to swallow a cat!", + "What a hog, to swallow a dog!", + "Just opened her throat and swallowed a goat!", + "I don't know how she swallowed a cow!", + "She's dead, of course!", + ] + + old_lady = "I know an old lady who swallowed a " + swallowed = "She swallowed the to catch the " die = "I don't know why she swallowed the fly. Perhaps she'll die." - song = '' - verse = '' - chain = '' + song = "" + verse = "" + chain = "" for number, animal in enumerate(animals): - verse = old_lady + animal + '.\n' + verse = old_lady + animal + ".\n" if number == 7: verse += phrases[6] else: if number == 0: - chain = swallowed + animal + '.\n' + chain = swallowed + animal + ".\n" elif number == 1: - verse += 'It' + phrases[0] + '\n' - chain = chain.replace('', animal) + verse += "It" + phrases[0] + "\n" + chain = chain.replace("", animal) verse += chain - chain = swallowed + animal + ' that' + phrases[0] + '\n' + chain + chain = swallowed + animal + " that" + phrases[0] + "\n" + chain else: - verse += phrases[number-1] + '\n' - chain = chain.replace('', animal) + verse += phrases[number - 1] + "\n" + chain = chain.replace("", animal) verse += chain - chain = swallowed + animal + '.\n' + chain + chain = swallowed + animal + ".\n" + chain - verse += die + '\n' + verse += die + "\n" - verse += '\n' + verse += "\n" song += verse return song def verses(letter): - return letter.replace('die.', 'die.slice').split('slice') + return letter.replace("die.", "die.slice").split("slice") def recite(start_verse, end_verse): - generated = [verse.strip().split('\n') for verse in verses(get_song())] + generated = [verse.strip().split("\n") for verse in verses(get_song())] if start_verse == end_verse: return generated[start_verse - 1] else: result = [] for idx in range(start_verse - 1, end_verse): - result += generated[idx] + [''] + result += generated[idx] + [""] # Pop out the last empty string result.pop() diff --git a/evaluator/datasets/polyglot_py_unpatched/forth/solution.py b/evaluator/datasets/polyglot_py_unpatched/forth/solution.py index 5e499acf5..71155eaa9 100644 --- a/evaluator/datasets/polyglot_py_unpatched/forth/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/forth/solution.py @@ -1,7 +1,8 @@ class StackUnderflowError(Exception): """Exception raised when Stack is not full. - message: explanation of the error. + message: explanation of the error. """ + def __init__(self, message): self.message = message @@ -18,18 +19,14 @@ def evaluate(input_data): if not input_data: return [] defines = {} - while input_data[0][:1] == ':': + while input_data[0][:1] == ":": values = input_data.pop(0).split() values.pop() values.pop(0) key = values.pop(0).lower() if is_integer(key): - raise ValueError('illegal operation') - defines[key] = [ - idx - for vivaldi in values - for idx in defines.get(vivaldi, [vivaldi]) - ] + raise ValueError("illegal operation") + defines[key] = [idx for vivaldi in values for idx in defines.get(vivaldi, [vivaldi])] stack = [] input_data = input_data[-1].split() while any(input_data): @@ -39,28 +36,28 @@ def evaluate(input_data): stack.append(int(word)) elif word in defines: input_data = defines[word] + input_data - elif word == '+': + elif word == "+": stack.append(stack.pop() + stack.pop()) - elif word == '-': + elif word == "-": stack.append(-stack.pop() + stack.pop()) - elif word == '*': + elif word == "*": stack.append(stack.pop() * stack.pop()) - elif word == '/': + elif word == "/": divisor = stack.pop() if divisor == 0: - raise ZeroDivisionError('divide by zero') + raise ZeroDivisionError("divide by zero") stack.append(int(stack.pop() / divisor)) - elif word == 'dup': + elif word == "dup": stack.append(stack[-1]) - elif word == 'drop': + elif word == "drop": stack.pop() - elif word == 'swap': + elif word == "swap": stack.append(stack[-2]) del stack[-3] - elif word == 'over': + elif word == "over": stack.append(stack[-2]) else: - raise ValueError('undefined operation') + raise ValueError("undefined operation") except IndexError as error: - raise StackUnderflowError('Insufficient number of items in stack') from error + raise StackUnderflowError("Insufficient number of items in stack") from error return stack diff --git a/evaluator/datasets/polyglot_py_unpatched/forth/tests.py b/evaluator/datasets/polyglot_py_unpatched/forth/tests.py index 3a528b87f..6df225079 100644 --- a/evaluator/datasets/polyglot_py_unpatched/forth/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/forth/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - evaluate, StackUnderflowError, + evaluate, ) @@ -24,17 +24,13 @@ def test_addition_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["+"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_addition_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 +"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_addition_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 2 3 +"]), [1, 5]) @@ -46,17 +42,13 @@ def test_subtraction_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["-"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_subtraction_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 -"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_subtraction_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 12 3 -"]), [1, 9]) @@ -68,17 +60,13 @@ def test_multiplication_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["*"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_multiplication_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 *"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_multiplication_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 2 3 *"]), [1, 6]) @@ -100,17 +88,13 @@ def test_division_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["/"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_division_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 /"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_division_more_than_two_values_on_the_stack(self): self.assertEqual(evaluate(["1 12 3 /"]), [1, 4]) @@ -137,9 +121,7 @@ def test_dup_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["dup"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_drop_removes_the_top_value_on_the_stack_if_it_is_the_only_one(self): self.assertEqual(evaluate(["1 drop"]), []) @@ -151,9 +133,7 @@ def test_drop_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["drop"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_swap_swaps_the_top_two_values_on_the_stack_if_they_are_the_only_ones(self): self.assertEqual(evaluate(["1 2 swap"]), [2, 1]) @@ -167,17 +147,13 @@ def test_swap_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["swap"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_swap_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 swap"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_over_copies_the_second_element_if_there_are_only_two(self): self.assertEqual(evaluate(["1 2 over"]), [1, 2, 1]) @@ -189,17 +165,13 @@ def test_over_errors_if_there_is_nothing_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["over"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_over_errors_if_there_is_only_one_value_on_the_stack(self): with self.assertRaises(StackUnderflowError) as err: evaluate(["1 over"]) self.assertEqual(type(err.exception), StackUnderflowError) - self.assertEqual( - str(err.exception.args[0]), "Insufficient number of items in stack" - ) + self.assertEqual(str(err.exception.args[0]), "Insufficient number of items in stack") def test_user_defined_words_can_consist_of_built_in_words(self): self.assertEqual(evaluate([": dup-twice dup dup ;", "1 dup-twice"]), [1, 1, 1]) @@ -208,9 +180,7 @@ def test_user_defined_words_execute_in_the_right_order(self): self.assertEqual(evaluate([": countup 1 2 3 ;", "countup"]), [1, 2, 3]) def test_user_defined_words_can_override_other_user_defined_words(self): - self.assertEqual( - evaluate([": foo dup ;", ": foo dup dup ;", "1 foo"]), [1, 1, 1] - ) + self.assertEqual(evaluate([": foo dup ;", ": foo dup dup ;", "1 foo"]), [1, 1, 1]) def test_user_defined_words_can_override_built_in_words(self): self.assertEqual(evaluate([": swap dup ;", "1 swap"]), [1, 1]) @@ -219,9 +189,7 @@ def test_user_defined_words_can_override_built_in_operators(self): self.assertEqual(evaluate([": + * ;", "3 4 +"]), [12]) def test_user_defined_words_can_use_different_words_with_the_same_name(self): - self.assertEqual( - evaluate([": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"]), [5, 6] - ) + self.assertEqual(evaluate([": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"]), [5, 6]) def test_user_defined_words_can_define_word_that_uses_word_with_the_same_name(self): self.assertEqual(evaluate([": foo 10 ;", ": foo foo 1 + ;", "foo"]), [11]) diff --git a/evaluator/datasets/polyglot_py_unpatched/gigasecond/tests.py b/evaluator/datasets/polyglot_py_unpatched/gigasecond/tests.py index 26c4a26d2..70afd0f3f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/gigasecond/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/gigasecond/tests.py @@ -2,8 +2,8 @@ # https://github.com/exercism/problem-specifications/tree/main/exercises/gigasecond/canonical-data.json # File last updated on 2023-07-19 -from datetime import datetime import unittest +from datetime import datetime from main import ( add, @@ -12,26 +12,16 @@ class GigasecondTest(unittest.TestCase): def test_date_only_specification_of_time(self): - self.assertEqual( - add(datetime(2011, 4, 25, 0, 0)), datetime(2043, 1, 1, 1, 46, 40) - ) + self.assertEqual(add(datetime(2011, 4, 25, 0, 0)), datetime(2043, 1, 1, 1, 46, 40)) def test_second_test_for_date_only_specification_of_time(self): - self.assertEqual( - add(datetime(1977, 6, 13, 0, 0)), datetime(2009, 2, 19, 1, 46, 40) - ) + self.assertEqual(add(datetime(1977, 6, 13, 0, 0)), datetime(2009, 2, 19, 1, 46, 40)) def test_third_test_for_date_only_specification_of_time(self): - self.assertEqual( - add(datetime(1959, 7, 19, 0, 0)), datetime(1991, 3, 27, 1, 46, 40) - ) + self.assertEqual(add(datetime(1959, 7, 19, 0, 0)), datetime(1991, 3, 27, 1, 46, 40)) def test_full_time_specified(self): - self.assertEqual( - add(datetime(2015, 1, 24, 22, 0)), datetime(2046, 10, 2, 23, 46, 40) - ) + self.assertEqual(add(datetime(2015, 1, 24, 22, 0)), datetime(2046, 10, 2, 23, 46, 40)) def test_full_time_with_day_roll_over(self): - self.assertEqual( - add(datetime(2015, 1, 24, 23, 59, 59)), datetime(2046, 10, 3, 1, 46, 39) - ) + self.assertEqual(add(datetime(2015, 1, 24, 23, 59, 59)), datetime(2046, 10, 3, 1, 46, 39)) diff --git a/evaluator/datasets/polyglot_py_unpatched/go-counting/main.py b/evaluator/datasets/polyglot_py_unpatched/go-counting/main.py index d1c689e70..a3ed7fa8e 100644 --- a/evaluator/datasets/polyglot_py_unpatched/go-counting/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/go-counting/main.py @@ -1,4 +1,3 @@ - class Board: """Count territories of each player in a Go game diff --git a/evaluator/datasets/polyglot_py_unpatched/go-counting/solution.py b/evaluator/datasets/polyglot_py_unpatched/go-counting/solution.py index c6ccd5121..cf7194dbc 100644 --- a/evaluator/datasets/polyglot_py_unpatched/go-counting/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/go-counting/solution.py @@ -1,7 +1,6 @@ - -BLACK = 'B' -WHITE = 'W' -NONE = '' +BLACK = "B" +WHITE = "W" +NONE = "" STONES = [BLACK, WHITE] DIRECTIONS = [(0, 1), (0, -1), (1, 0), (-1, 0)] @@ -28,10 +27,13 @@ def walk(self, width, height, visited_territory=None, visited_coords=None, visit return (visited_territory, visited_stones + [stone]) else: # s is empty for direction in DIRECTIONS: - visited = self.walk(width + direction[0], height + direction[1], - visited_territory + [(width, height)], - visited_coords + [(width, height)], - visited_stones) + visited = self.walk( + width + direction[0], + height + direction[1], + visited_territory + [(width, height)], + visited_coords + [(width, height)], + visited_stones, + ) visited_territory = visited[0] visited_stones = visited[1] @@ -39,7 +41,7 @@ def walk(self, width, height, visited_territory=None, visited_coords=None, visit def territory(self, x, y): if not self.valid(x, y): - raise ValueError('Invalid coordinate') + raise ValueError("Invalid coordinate") if self.board[y][x] in STONES: return (NONE, set()) @@ -52,11 +54,11 @@ def territory(self, x, y): def territories(self): owners = STONES + [NONE] - result = {owner:set() for owner in owners} + result = {owner: set() for owner in owners} visited = set() for row in range(self.height): for column in range(self.width): - if not (column, row) in visited: + if (column, row) not in visited: owner, owned_territories = self.territory(column, row) result[owner].update(owned_territories) visited.update(owned_territories) diff --git a/evaluator/datasets/polyglot_py_unpatched/go-counting/tests.py b/evaluator/datasets/polyglot_py_unpatched/go-counting/tests.py index d69cad7a0..86d4a6a67 100644 --- a/evaluator/datasets/polyglot_py_unpatched/go-counting/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/go-counting/tests.py @@ -5,10 +5,10 @@ import unittest from main import ( - Board, - WHITE, BLACK, NONE, + WHITE, + Board, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/grade-school/solution.py b/evaluator/datasets/polyglot_py_unpatched/grade-school/solution.py index fc974919e..501168218 100644 --- a/evaluator/datasets/polyglot_py_unpatched/grade-school/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/grade-school/solution.py @@ -19,7 +19,6 @@ def add_student(self, name, grade): else: self.add.append(False) - def roster(self, grade=0): grades_roster = defaultdict(list) diff --git a/evaluator/datasets/polyglot_py_unpatched/grains/solution.py b/evaluator/datasets/polyglot_py_unpatched/grains/solution.py index 8610d32b5..7b5aaa4ba 100644 --- a/evaluator/datasets/polyglot_py_unpatched/grains/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/grains/solution.py @@ -1,13 +1,13 @@ def square(number): if number == 0: - raise ValueError('square must be between 1 and 64') + raise ValueError("square must be between 1 and 64") elif number < 0: - raise ValueError('square must be between 1 and 64') + raise ValueError("square must be between 1 and 64") elif number > 64: - raise ValueError('square must be between 1 and 64') + raise ValueError("square must be between 1 and 64") return 2 ** (number - 1) def total(): - return (2 ** 64) - 1 + return (2**64) - 1 diff --git a/evaluator/datasets/polyglot_py_unpatched/grep/solution.py b/evaluator/datasets/polyglot_py_unpatched/grep/solution.py index e0ffbfe2d..f2ee5348e 100644 --- a/evaluator/datasets/polyglot_py_unpatched/grep/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/grep/solution.py @@ -1,24 +1,24 @@ def matches(line, pattern, flags): - if '-i' in flags: # case-insensitive + if "-i" in flags: # case-insensitive line = line.lower() pattern = pattern.lower() - if '-x' in flags: # match entire lines + if "-x" in flags: # match entire lines if len(pattern) != len(line.rstrip()): - return '-v' in flags + return "-v" in flags - if '-v' in flags: # invert matching + if "-v" in flags: # invert matching return pattern not in line return pattern in line def format_files(matched_lines): - result = '' + result = "" for file_name, _, _ in matched_lines: if file_name not in result: - result += file_name + '\n' + result += file_name + "\n" return result @@ -27,31 +27,31 @@ def format_lines(matched_lines, flags, files): result = [] for file_name, line_number, line in matched_lines: - line_result = '' + line_result = "" if len(files) > 1: - line_result += file_name + ':' + line_result += file_name + ":" - if '-n' in flags: - line_result += str(line_number) + ':' + if "-n" in flags: + line_result += str(line_number) + ":" line_result += line result.append(line_result) - return ''.join(result) + return "".join(result) def grep(pattern, flags, files): matched_lines = [] for file_name in files: - with open(file_name, encoding='utf-8') as f: + with open(file_name, encoding="utf-8") as f: for line_number, line in enumerate(f.readlines(), start=1): if matches(line, pattern, flags): matched_lines.append((file_name, line_number, line)) - if '-l' in flags: + if "-l" in flags: return format_files(matched_lines) return format_lines(matched_lines, flags, files) diff --git a/evaluator/datasets/polyglot_py_unpatched/grep/tests.py b/evaluator/datasets/polyglot_py_unpatched/grep/tests.py index 3f62f6ba2..68d8dc34c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/grep/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/grep/tests.py @@ -4,11 +4,11 @@ import io import unittest +from unittest import mock from main import ( grep, ) -from unittest import mock FILE_TEXT = { "iliad.txt": """Achilles sing, O Goddess! Peleus' son; @@ -42,9 +42,7 @@ def open_mock(fname, *args, **kwargs): try: return io.StringIO(FILE_TEXT[fname]) except KeyError: - raise RuntimeError( - "Expected one of {0!r}: got {1!r}".format(list(FILE_TEXT.keys()), fname) - ) + raise RuntimeError("Expected one of {0!r}: got {1!r}".format(list(FILE_TEXT.keys()), fname)) @mock.patch("main.open", name="open", side_effect=open_mock, create=True) @@ -52,9 +50,7 @@ def open_mock(fname, *args, **kwargs): class GrepTest(unittest.TestCase): # Test grepping a single file def test_one_file_one_match_no_flags(self, mock_file, mock_open): - self.assertMultiLineEqual( - grep("Agamemnon", "", ["iliad.txt"]), "Of Atreus, Agamemnon, King of men.\n" - ) + self.assertMultiLineEqual(grep("Agamemnon", "", ["iliad.txt"]), "Of Atreus, Agamemnon, King of men.\n") def test_one_file_one_match_print_line_numbers_flag(self, mock_file, mock_open): self.assertMultiLineEqual( @@ -69,15 +65,11 @@ def test_one_file_one_match_case_insensitive_flag(self, mock_file, mock_open): ) def test_one_file_one_match_print_file_names_flag(self, mock_file, mock_open): - self.assertMultiLineEqual( - grep("Forbidden", "-l", ["paradise-lost.txt"]), "paradise-lost.txt\n" - ) + self.assertMultiLineEqual(grep("Forbidden", "-l", ["paradise-lost.txt"]), "paradise-lost.txt\n") def test_one_file_one_match_match_entire_lines_flag(self, mock_file, mock_open): self.assertMultiLineEqual( - grep( - "With loss of Eden, till one greater Man", "-x", ["paradise-lost.txt"] - ), + grep("With loss of Eden, till one greater Man", "-x", ["paradise-lost.txt"]), "With loss of Eden, till one greater Man\n", ) @@ -95,9 +87,7 @@ def test_one_file_several_matches_no_flags(self, mock_file, mock_open): "The worst that may befall me in this case,\n", ) - def test_one_file_several_matches_print_line_numbers_flag( - self, mock_file, mock_open - ): + def test_one_file_several_matches_print_line_numbers_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep("may", "-n", ["midsummer-night.txt"]), "3:Nor how it may concern my modesty,\n" @@ -105,16 +95,13 @@ def test_one_file_several_matches_print_line_numbers_flag( "6:The worst that may befall me in this case,\n", ) - def test_one_file_several_matches_match_entire_lines_flag( - self, mock_file, mock_open - ): + def test_one_file_several_matches_match_entire_lines_flag(self, mock_file, mock_open): self.assertMultiLineEqual(grep("may", "-x", ["midsummer-night.txt"]), "") def test_one_file_several_matches_case_insensitive_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep("ACHILLES", "-i", ["iliad.txt"]), - "Achilles sing, O Goddess! Peleus' son;\n" - "The noble Chief Achilles from the son\n", + "Achilles sing, O Goddess! Peleus' son;\nThe noble Chief Achilles from the son\n", ) def test_one_file_several_matches_inverted_flag(self, mock_file, mock_open): @@ -130,14 +117,10 @@ def test_one_file_several_matches_inverted_flag(self, mock_file, mock_open): def test_one_file_no_matches_various_flags(self, mock_file, mock_open): self.assertMultiLineEqual(grep("Gandalf", "-n -l -x -i", ["iliad.txt"]), "") - def test_one_file_one_match_file_flag_takes_precedence_over_line_flag( - self, mock_file, mock_open - ): + def test_one_file_one_match_file_flag_takes_precedence_over_line_flag(self, mock_file, mock_open): self.assertMultiLineEqual(grep("ten", "-n -l", ["iliad.txt"]), "iliad.txt\n") - def test_one_file_several_matches_inverted_and_match_entire_lines_flags( - self, mock_file, mock_open - ): + def test_one_file_several_matches_inverted_and_match_entire_lines_flags(self, mock_file, mock_open): self.assertMultiLineEqual( grep("Illustrious into Ades premature,", "-x -v", ["iliad.txt"]), "Achilles sing, O Goddess! Peleus' son;\n" @@ -169,13 +152,9 @@ def test_multiple_files_several_matches_no_flags(self, mock_file, mock_open): "midsummer-night.txt:The worst that may befall me in this case,\n", ) - def test_multiple_files_several_matches_print_line_numbers_flag( - self, mock_file, mock_open - ): + def test_multiple_files_several_matches_print_line_numbers_flag(self, mock_file, mock_open): self.assertMultiLineEqual( - grep( - "that", "-n", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"] - ), + grep("that", "-n", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"]), "midsummer-night.txt:5:But I beseech your grace that I may know\n" "midsummer-night.txt:6:The worst that may befall me in this case,\n" "paradise-lost.txt:2:Of that Forbidden Tree, whose mortal tast\n" @@ -184,15 +163,11 @@ def test_multiple_files_several_matches_print_line_numbers_flag( def test_multiple_files_one_match_print_file_names_flag(self, mock_file, mock_open): self.assertMultiLineEqual( - grep( - "who", "-l", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"] - ), - "iliad.txt\n" "paradise-lost.txt\n", + grep("who", "-l", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"]), + "iliad.txt\nparadise-lost.txt\n", ) - def test_multiple_files_several_matches_case_insensitive_flag( - self, mock_file, mock_open - ): + def test_multiple_files_several_matches_case_insensitive_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep("TO", "-i", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"]), "iliad.txt:Caused to Achaia's host, sent many a soul\n" @@ -215,9 +190,7 @@ def test_multiple_files_several_matches_inverted_flag(self, mock_file, mock_open "midsummer-night.txt:If I refuse to wed Demetrius.\n", ) - def test_multiple_files_one_match_match_entire_lines_flag( - self, mock_file, mock_open - ): + def test_multiple_files_one_match_match_entire_lines_flag(self, mock_file, mock_open): self.assertMultiLineEqual( grep( "But I beseech your grace that I may know", @@ -256,12 +229,10 @@ def test_multiple_files_several_matches_file_flag_takes_precedence_over_line_num "-n -l", ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], ), - "iliad.txt\n" "paradise-lost.txt\n", + "iliad.txt\nparadise-lost.txt\n", ) - def test_multiple_files_several_matches_inverted_and_match_entire_lines_flags( - self, mock_file, mock_open - ): + def test_multiple_files_several_matches_inverted_and_match_entire_lines_flags(self, mock_file, mock_open): self.assertMultiLineEqual( grep( "Illustrious into Ades premature,", diff --git a/evaluator/datasets/polyglot_py_unpatched/hamming/solution.py b/evaluator/datasets/polyglot_py_unpatched/hamming/solution.py index d6a3848ef..943c580fa 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hamming/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/hamming/solution.py @@ -1,5 +1,5 @@ def distance(strand_a, strand_b): if len(strand_a) != len(strand_b): - raise ValueError('Strands must be of equal length.') + raise ValueError("Strands must be of equal length.") return sum(a_part != b_part for a_part, b_part in zip(strand_a, strand_b)) diff --git a/evaluator/datasets/polyglot_py_unpatched/hangman/main.py b/evaluator/datasets/polyglot_py_unpatched/hangman/main.py index 5f0db276c..9385d240b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hangman/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/hangman/main.py @@ -1,8 +1,8 @@ # Game status categories # Change the values as you see fit -STATUS_WIN = 'win' -STATUS_LOSE = 'lose' -STATUS_ONGOING = 'ongoing' +STATUS_WIN = "win" +STATUS_LOSE = "lose" +STATUS_ONGOING = "ongoing" class Hangman: diff --git a/evaluator/datasets/polyglot_py_unpatched/hangman/solution.py b/evaluator/datasets/polyglot_py_unpatched/hangman/solution.py index b03168609..89fdd3a09 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hangman/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/hangman/solution.py @@ -1,6 +1,6 @@ -STATUS_WIN = 'win' -STATUS_LOSE = 'lose' -STATUS_ONGOING = 'ongoing' +STATUS_WIN = "win" +STATUS_LOSE = "lose" +STATUS_ONGOING = "ongoing" class Hangman: @@ -8,24 +8,24 @@ def __init__(self, word): self.remaining_guesses = 9 self.status = STATUS_ONGOING self.word = word - self.masked_word = '' + self.masked_word = "" self.guesses = [] for _ in self.word: - self.masked_word += '_' + self.masked_word += "_" def guess(self, char): if self.status != STATUS_ONGOING: - raise ValueError('The game has already ended.') + raise ValueError("The game has already ended.") self.update_remaining_guesses(char) self.update_masked_word() self.update_status() def update_masked_word(self): - self.masked_word = '' + self.masked_word = "" for idx in self.word: if idx not in self.guesses: - self.masked_word += '_' + self.masked_word += "_" else: self.masked_word += idx diff --git a/evaluator/datasets/polyglot_py_unpatched/hangman/tests.py b/evaluator/datasets/polyglot_py_unpatched/hangman/tests.py index 3273c597b..a189a507e 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hangman/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/hangman/tests.py @@ -3,95 +3,95 @@ import main as hangman from main import Hangman - # Tests adapted from csharp//hangman/HangmanTest.cs + class HangmanTests(unittest.TestCase): def test_initially_9_failures_are_allowed(self): - game = Hangman('foo') + game = Hangman("foo") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) def test_initially_no_letters_are_guessed(self): - game = Hangman('foo') + game = Hangman("foo") - self.assertEqual(game.get_masked_word(), '___') + self.assertEqual(game.get_masked_word(), "___") def test_after_10_failures_the_game_is_over(self): - game = Hangman('foo') + game = Hangman("foo") for i in range(10): - game.guess('x') + game.guess("x") self.assertEqual(game.get_status(), hangman.STATUS_LOSE) with self.assertRaises(ValueError) as err: - game.guess('x') + game.guess("x") self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "The game has already ended.") def test_feeding_a_correct_letter_removes_underscores(self): - game = Hangman('foobar') + game = Hangman("foobar") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '___b__') + self.assertEqual(game.get_masked_word(), "___b__") - game.guess('o') + game.guess("o") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '_oob__') + self.assertEqual(game.get_masked_word(), "_oob__") def test_feeding_a_correct_letter_twice_counts_as_a_failure(self): - game = Hangman('foobar') + game = Hangman("foobar") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 9) - self.assertEqual(game.get_masked_word(), '___b__') + self.assertEqual(game.get_masked_word(), "___b__") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 8) - self.assertEqual(game.get_masked_word(), '___b__') + self.assertEqual(game.get_masked_word(), "___b__") def test_getting_all_the_letters_right_makes_for_a_win(self): - game = Hangman('hello') + game = Hangman("hello") - game.guess('b') + game.guess("b") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 8) - self.assertEqual(game.get_masked_word(), '_____') + self.assertEqual(game.get_masked_word(), "_____") - game.guess('e') + game.guess("e") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 8) - self.assertEqual(game.get_masked_word(), '_e___') + self.assertEqual(game.get_masked_word(), "_e___") - game.guess('l') + game.guess("l") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 8) - self.assertEqual(game.get_masked_word(), '_ell_') + self.assertEqual(game.get_masked_word(), "_ell_") - game.guess('o') + game.guess("o") self.assertEqual(game.get_status(), hangman.STATUS_ONGOING) self.assertEqual(game.remaining_guesses, 8) - self.assertEqual(game.get_masked_word(), '_ello') + self.assertEqual(game.get_masked_word(), "_ello") - game.guess('h') + game.guess("h") self.assertEqual(game.get_status(), hangman.STATUS_WIN) - self.assertEqual(game.get_masked_word(), 'hello') + self.assertEqual(game.get_masked_word(), "hello") with self.assertRaises(ValueError) as err: - game.guess('x') + game.guess("x") self.assertEqual(type(err.exception), ValueError) self.assertEqual(err.exception.args[0], "The game has already ended.") def test_winning_on_last_guess_still_counts_as_a_win(self): - game = Hangman('aaa') - for ch in 'bcdefghij': + game = Hangman("aaa") + for ch in "bcdefghij": game.guess(ch) - game.guess('a') + game.guess("a") self.assertEqual(game.remaining_guesses, 0) self.assertEqual(game.get_status(), hangman.STATUS_WIN) - self.assertEqual(game.get_masked_word(), 'aaa') + self.assertEqual(game.get_masked_word(), "aaa") diff --git a/evaluator/datasets/polyglot_py_unpatched/hello-world/main.py b/evaluator/datasets/polyglot_py_unpatched/hello-world/main.py index adaa6c2c9..dcc91b523 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hello-world/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/hello-world/main.py @@ -1,2 +1,2 @@ def hello(): - return 'Goodbye, Mars!' + return "Goodbye, Mars!" diff --git a/evaluator/datasets/polyglot_py_unpatched/hello-world/solution.py b/evaluator/datasets/polyglot_py_unpatched/hello-world/solution.py index d695ea115..dea05ae20 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hello-world/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/hello-world/solution.py @@ -1,2 +1,2 @@ def hello(): - return 'Hello, World!' + return "Hello, World!" diff --git a/evaluator/datasets/polyglot_py_unpatched/hexadecimal/solution.py b/evaluator/datasets/polyglot_py_unpatched/hexadecimal/solution.py index 672321a89..6499b4d93 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hexadecimal/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/hexadecimal/solution.py @@ -3,8 +3,7 @@ def hexa(hex_string): hex_string = hex_string.lower() - if set(hex_string) - set('0123456789abcdef'): - raise ValueError('Invalid hexadecimal string') - digits = [ord(letter) - ord('a') + 10 if letter in 'abcdef' else ord(letter) - ord('0') - for letter in hex_string] + if set(hex_string) - set("0123456789abcdef"): + raise ValueError("Invalid hexadecimal string") + digits = [ord(letter) - ord("a") + 10 if letter in "abcdef" else ord(letter) - ord("0") for letter in hex_string] return reduce(lambda var_1, var_2: var_1 * 16 + var_2, digits, 0) diff --git a/evaluator/datasets/polyglot_py_unpatched/hexadecimal/tests.py b/evaluator/datasets/polyglot_py_unpatched/hexadecimal/tests.py index a7eb7603b..390e97ce3 100644 --- a/evaluator/datasets/polyglot_py_unpatched/hexadecimal/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/hexadecimal/tests.py @@ -8,40 +8,40 @@ class HexadecimalTest(unittest.TestCase): def test_valid_hexa1(self): - self.assertEqual(hexa('1'), 1) + self.assertEqual(hexa("1"), 1) def test_valid_hexa2(self): - self.assertEqual(hexa('c'), 12) + self.assertEqual(hexa("c"), 12) def test_valid_hexa3(self): - self.assertEqual(hexa('10'), 16) + self.assertEqual(hexa("10"), 16) def test_valid_hexa4(self): - self.assertEqual(hexa('af'), 175) + self.assertEqual(hexa("af"), 175) def test_valid_hexa5(self): - self.assertEqual(hexa('100'), 256) + self.assertEqual(hexa("100"), 256) def test_valid_hexa6(self): - self.assertEqual(hexa('19ACE'), 105166) + self.assertEqual(hexa("19ACE"), 105166) def test_valid_hexa7(self): - self.assertEqual(hexa('000000'), 0) + self.assertEqual(hexa("000000"), 0) def test_valid_hexa8(self): - self.assertEqual(hexa('ffff00'), 16776960) + self.assertEqual(hexa("ffff00"), 16776960) def test_valid_hexa9(self): - self.assertEqual(hexa('00fff0'), 65520) + self.assertEqual(hexa("00fff0"), 65520) def test_invalid_hexa(self): with self.assertRaisesWithMessage(ValueError): - hexa('carrot') + hexa("carrot") # Utility functions def assertRaisesWithMessage(self, exception): return self.assertRaisesRegex(exception, r".+") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/house/solution.py b/evaluator/datasets/polyglot_py_unpatched/house/solution.py index 30e289b91..9d992c513 100644 --- a/evaluator/datasets/polyglot_py_unpatched/house/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/house/solution.py @@ -1,23 +1,24 @@ -PARTS = [('lay in', 'the house that Jack built.'), - ('ate', 'the malt'), - ('killed', 'the rat'), - ('worried', 'the cat'), - ('tossed', 'the dog'), - ('milked', 'the cow with the crumpled horn'), - ('kissed', 'the maiden all forlorn'), - ('married', 'the man all tattered and torn'), - ('woke', 'the priest all shaven and shorn'), - ('kept', 'the rooster that crowed in the morn'), - ('belonged to', 'the farmer sowing his corn'), - ('', 'the horse and the hound and the horn')] +PARTS = [ + ("lay in", "the house that Jack built."), + ("ate", "the malt"), + ("killed", "the rat"), + ("worried", "the cat"), + ("tossed", "the dog"), + ("milked", "the cow with the crumpled horn"), + ("kissed", "the maiden all forlorn"), + ("married", "the man all tattered and torn"), + ("woke", "the priest all shaven and shorn"), + ("kept", "the rooster that crowed in the morn"), + ("belonged to", "the farmer sowing his corn"), + ("", "the horse and the hound and the horn"), +] def verse(verse_num): - verse = [f'This is {PARTS[verse_num][1]}'] - verse.extend(['that {0} {1}'.format(*PARTS[idx]) - for idx in range(verse_num - 1, -1, -1)]) - return ' '.join(verse) + verse = [f"This is {PARTS[verse_num][1]}"] + verse.extend(["that {0} {1}".format(*PARTS[idx]) for idx in range(verse_num - 1, -1, -1)]) + return " ".join(verse) def recite(start_verse, end_verse): - return [verse(verse_num) for verse_num in range(start_verse-1, end_verse)] + return [verse(verse_num) for verse_num in range(start_verse - 1, end_verse)] diff --git a/evaluator/datasets/polyglot_py_unpatched/house/tests.py b/evaluator/datasets/polyglot_py_unpatched/house/tests.py index 74372a416..01d4a8589 100644 --- a/evaluator/datasets/polyglot_py_unpatched/house/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/house/tests.py @@ -14,24 +14,18 @@ def test_verse_one_the_house_that_jack_built(self): self.assertEqual(recite(1, 1), ["This is the house that Jack built."]) def test_verse_two_the_malt_that_lay(self): - self.assertEqual( - recite(2, 2), ["This is the malt that lay in the house that Jack built."] - ) + self.assertEqual(recite(2, 2), ["This is the malt that lay in the house that Jack built."]) def test_verse_three_the_rat_that_ate(self): self.assertEqual( recite(3, 3), - [ - "This is the rat that ate the malt that lay in the house that Jack built." - ], + ["This is the rat that ate the malt that lay in the house that Jack built."], ) def test_verse_four_the_cat_that_killed(self): self.assertEqual( recite(4, 4), - [ - "This is the cat that killed the rat that ate the malt that lay in the house that Jack built." - ], + ["This is the cat that killed the rat that ate the malt that lay in the house that Jack built."], ) def test_verse_five_the_dog_that_worried(self): diff --git a/evaluator/datasets/polyglot_py_unpatched/isbn-verifier/solution.py b/evaluator/datasets/polyglot_py_unpatched/isbn-verifier/solution.py index f2f14f394..8e5441155 100644 --- a/evaluator/datasets/polyglot_py_unpatched/isbn-verifier/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/isbn-verifier/solution.py @@ -1,7 +1,7 @@ def is_valid(isbn): - chars = list(isbn.replace('-', '')) - if chars and chars[-1] == 'X': - chars[-1] = '10' + chars = list(isbn.replace("-", "")) + if chars and chars[-1] == "X": + chars[-1] = "10" if not len(chars) == 10 or not all(char.isdigit() for char in chars): return False indices = list(range(10, 0, -1)) diff --git a/evaluator/datasets/polyglot_py_unpatched/killer-sudoku-helper/solution.py b/evaluator/datasets/polyglot_py_unpatched/killer-sudoku-helper/solution.py index ac3a7f879..a789bf9e2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/killer-sudoku-helper/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/killer-sudoku-helper/solution.py @@ -1,10 +1,9 @@ import itertools + def combinations(target, size, exclude): result = [] - possible = [index for index in - range(1, int((target ** 2 /size) ** 0.6)) - if index not in exclude] + possible = [index for index in range(1, int((target**2 / size) ** 0.6)) if index not in exclude] if size == 1: return [[target]] diff --git a/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/solution.py b/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/solution.py index 262c6ad6e..33f66e914 100644 --- a/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/solution.py @@ -1,20 +1,19 @@ class Garden: - STUDENTS = [ - 'Alice', - 'Bob', - 'Charlie', - 'David', - 'Eve', - 'Fred', - 'Ginny', - 'Harriet', - 'Ileana', - 'Joseph', - 'Kincaid', - 'Larry', + "Alice", + "Bob", + "Charlie", + "David", + "Eve", + "Fred", + "Ginny", + "Harriet", + "Ileana", + "Joseph", + "Kincaid", + "Larry", ] - PLANTS = {'C': 'Clover', 'G': 'Grass', 'R': 'Radishes', 'V': 'Violets'} + PLANTS = {"C": "Clover", "G": "Grass", "R": "Radishes", "V": "Violets"} def __init__(self, diagram, students=None): students = sorted(students or self.STUDENTS) @@ -24,12 +23,8 @@ def __init__(self, diagram, students=None): start = idx * 2 stop = start + 2 self.cups.setdefault(student, []) - self.cups[student].extend( - self.PLANTS[plant] for plant in front[start:stop] - ) - self.cups[student].extend( - self.PLANTS[plant] for plant in back[start:stop] - ) + self.cups[student].extend(self.PLANTS[plant] for plant in front[start:stop]) + self.cups[student].extend(self.PLANTS[plant] for plant in back[start:stop]) def plants(self, student): return self.cups.get(student, []) diff --git a/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/tests.py b/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/tests.py index a7fc25e16..24a0aaa21 100644 --- a/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/kindergarten-garden/tests.py @@ -12,21 +12,15 @@ class KindergartenGardenTest(unittest.TestCase): def test_partial_garden_garden_with_single_student(self): garden = Garden("RC\nGG") - self.assertEqual( - garden.plants("Alice"), ["Radishes", "Clover", "Grass", "Grass"] - ) + self.assertEqual(garden.plants("Alice"), ["Radishes", "Clover", "Grass", "Grass"]) def test_partial_garden_different_garden_with_single_student(self): garden = Garden("VC\nRC") - self.assertEqual( - garden.plants("Alice"), ["Violets", "Clover", "Radishes", "Clover"] - ) + self.assertEqual(garden.plants("Alice"), ["Violets", "Clover", "Radishes", "Clover"]) def test_partial_garden_garden_with_two_students(self): garden = Garden("VVCG\nVVRC") - self.assertEqual( - garden.plants("Bob"), ["Clover", "Grass", "Radishes", "Clover"] - ) + self.assertEqual(garden.plants("Bob"), ["Clover", "Grass", "Radishes", "Clover"]) def test_partial_garden_second_student_s_garden(self): garden = Garden("VVCCGG\nVVCCGG") @@ -38,9 +32,7 @@ def test_partial_garden_third_student_s_garden(self): def test_full_garden_for_alice_first_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Alice"), ["Violets", "Radishes", "Violets", "Radishes"] - ) + self.assertEqual(garden.plants("Alice"), ["Violets", "Radishes", "Violets", "Radishes"]) def test_full_garden_for_bob_second_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") @@ -48,15 +40,11 @@ def test_full_garden_for_bob_second_student_s_garden(self): def test_full_garden_for_charlie(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Charlie"), ["Violets", "Violets", "Clover", "Grass"] - ) + self.assertEqual(garden.plants("Charlie"), ["Violets", "Violets", "Clover", "Grass"]) def test_full_garden_for_david(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("David"), ["Radishes", "Violets", "Clover", "Radishes"] - ) + self.assertEqual(garden.plants("David"), ["Radishes", "Violets", "Clover", "Radishes"]) def test_full_garden_for_eve(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") @@ -64,9 +52,7 @@ def test_full_garden_for_eve(self): def test_full_garden_for_fred(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Fred"), ["Grass", "Clover", "Violets", "Clover"] - ) + self.assertEqual(garden.plants("Fred"), ["Grass", "Clover", "Violets", "Clover"]) def test_full_garden_for_ginny(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") @@ -74,48 +60,30 @@ def test_full_garden_for_ginny(self): def test_full_garden_for_harriet(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Harriet"), ["Violets", "Radishes", "Radishes", "Violets"] - ) + self.assertEqual(garden.plants("Harriet"), ["Violets", "Radishes", "Radishes", "Violets"]) def test_full_garden_for_ileana(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Ileana"), ["Grass", "Clover", "Violets", "Clover"] - ) + self.assertEqual(garden.plants("Ileana"), ["Grass", "Clover", "Violets", "Clover"]) def test_full_garden_for_joseph(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Joseph"), ["Violets", "Clover", "Violets", "Grass"] - ) + self.assertEqual(garden.plants("Joseph"), ["Violets", "Clover", "Violets", "Grass"]) def test_full_garden_for_kincaid_second_to_last_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Kincaid"), ["Grass", "Clover", "Clover", "Grass"] - ) + self.assertEqual(garden.plants("Kincaid"), ["Grass", "Clover", "Clover", "Grass"]) def test_full_garden_for_larry_last_student_s_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") - self.assertEqual( - garden.plants("Larry"), ["Grass", "Violets", "Clover", "Violets"] - ) + self.assertEqual(garden.plants("Larry"), ["Grass", "Violets", "Clover", "Violets"]) # Additional tests for this track def test_students_are_unordered_first_student(self): - garden = Garden( - "VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"] - ) - self.assertEqual( - garden.plants("Patricia"), ["Violets", "Clover", "Radishes", "Violets"] - ) + garden = Garden("VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"]) + self.assertEqual(garden.plants("Patricia"), ["Violets", "Clover", "Radishes", "Violets"]) def test_students_are_unordered_last_student(self): - garden = Garden( - "VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"] - ) - self.assertEqual( - garden.plants("Xander"), ["Radishes", "Grass", "Clover", "Violets"] - ) + garden = Garden("VCRRGVRG\nRVGCCGCV", students=["Samantha", "Patricia", "Xander", "Roger"]) + self.assertEqual(garden.plants("Xander"), ["Radishes", "Grass", "Clover", "Violets"]) diff --git a/evaluator/datasets/polyglot_py_unpatched/knapsack/solution.py b/evaluator/datasets/polyglot_py_unpatched/knapsack/solution.py index 548dd5a2b..017d160f7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/knapsack/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/knapsack/solution.py @@ -1,12 +1,10 @@ def maximum_value(maximum_weight, items): - totals = [[0 for _ in range(len(items) + 1)] - for _ in range(maximum_weight + 1)] + totals = [[0 for _ in range(len(items) + 1)] for _ in range(maximum_weight + 1)] for weight in range(1, maximum_weight + 1): for index, item in enumerate(items, 1): - if item['weight'] <= weight: - value = item['value'] + \ - totals[weight - item['weight']][index - 1] + if item["weight"] <= weight: + value = item["value"] + totals[weight - item["weight"]][index - 1] value_without_item = totals[weight][index - 1] totals[weight][index] = max(value, value_without_item) diff --git a/evaluator/datasets/polyglot_py_unpatched/largest-series-product/solution.py b/evaluator/datasets/polyglot_py_unpatched/largest-series-product/solution.py index f7cfc2310..9f583c677 100644 --- a/evaluator/datasets/polyglot_py_unpatched/largest-series-product/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/largest-series-product/solution.py @@ -5,19 +5,18 @@ def slices(series, size): if not size <= len(series): - raise ValueError('span must not exceed string length') + raise ValueError("span must not exceed string length") elif not 0 < size: - raise ValueError('span must not be negative') + raise ValueError("span must not be negative") elif not all(item.isdigit() for item in series): - raise ValueError('digits input must only contain digits') + raise ValueError("digits input must only contain digits") numbers = [int(digit) for digit in series] - return [numbers[idx:idx + size] - for idx in range(len(numbers) - size + 1)] + return [numbers[idx : idx + size] for idx in range(len(numbers) - size + 1)] def largest_product(series, size): if size == 0: return 1 - return max(reduce(mul, slice) for slice in slices(series, size)) \ No newline at end of file + return max(reduce(mul, slice) for slice in slices(series, size)) diff --git a/evaluator/datasets/polyglot_py_unpatched/ledger/main.py b/evaluator/datasets/polyglot_py_unpatched/ledger/main.py index c0dcf94f7..e7504fff0 100644 --- a/evaluator/datasets/polyglot_py_unpatched/ledger/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/ledger/main.py @@ -11,27 +11,27 @@ def __init__(self): def create_entry(date, description, change): entry = LedgerEntry() - entry.date = datetime.strptime(date, '%Y-%m-%d') + entry.date = datetime.strptime(date, "%Y-%m-%d") entry.description = description entry.change = change return entry def format_entries(currency, locale, entries): - if locale == 'en_US': + if locale == "en_US": # Generate Header Row - table = 'Date' + table = "Date" for _ in range(7): - table += ' ' - table += '| Description' + table += " " + table += "| Description" for _ in range(15): - table += ' ' - table += '| Change' + table += " " + table += "| Change" for _ in range(7): - table += ' ' + table += " " while len(entries) > 0: - table += '\n' + table += "\n" # Find next entry in order min_entry_index = -1 @@ -44,16 +44,13 @@ def format_entries(currency, locale, entries): if entry.date < min_entry.date: min_entry_index = i continue - if ( - entry.date == min_entry.date and - entry.change < min_entry.change - ): + if entry.date == min_entry.date and entry.change < min_entry.change: min_entry_index = i continue if ( - entry.date == min_entry.date and - entry.change == min_entry.change and - entry.description < min_entry.description + entry.date == min_entry.date + and entry.change == min_entry.change + and entry.description < min_entry.description ): min_entry_index = i continue @@ -64,117 +61,117 @@ def format_entries(currency, locale, entries): month = entry.date.month month = str(month) if len(month) < 2: - month = '0' + month + month = "0" + month date_str = month - date_str += '/' + date_str += "/" day = entry.date.day day = str(day) if len(day) < 2: - day = '0' + day + day = "0" + day date_str += day - date_str += '/' + date_str += "/" year = entry.date.year year = str(year) while len(year) < 4: - year = '0' + year + year = "0" + year date_str += year table += date_str - table += ' | ' + table += " | " # Write entry description to table # Truncate if necessary if len(entry.description) > 25: for i in range(22): table += entry.description[i] - table += '...' + table += "..." else: for i in range(25): if len(entry.description) > i: table += entry.description[i] else: - table += ' ' - table += ' | ' + table += " " + table += " | " # Write entry change to table - if currency == 'USD': - change_str = '' + if currency == "USD": + change_str = "" if entry.change < 0: - change_str = '(' - change_str += '$' + change_str = "(" + change_str += "$" change_dollar = abs(int(entry.change / 100.0)) dollar_parts = [] while change_dollar > 0: dollar_parts.insert(0, str(change_dollar % 1000)) change_dollar = change_dollar // 1000 if len(dollar_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += dollar_parts[0] dollar_parts.pop(0) if len(dollar_parts) == 0: break - change_str += ',' - change_str += '.' + change_str += "," + change_str += "." change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents if entry.change < 0: - change_str += ')' + change_str += ")" else: - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str - elif currency == 'EUR': - change_str = '' + elif currency == "EUR": + change_str = "" if entry.change < 0: - change_str = '(' - change_str += u'€' + change_str = "(" + change_str += "€" change_euro = abs(int(entry.change / 100.0)) euro_parts = [] while change_euro > 0: euro_parts.insert(0, str(change_euro % 1000)) change_euro = change_euro // 1000 if len(euro_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += euro_parts[0] euro_parts.pop(0) if len(euro_parts) == 0: break - change_str += ',' - change_str += '.' + change_str += "," + change_str += "." change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents if entry.change < 0: - change_str += ')' + change_str += ")" else: - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str return table - elif locale == 'nl_NL': + elif locale == "nl_NL": # Generate Header Row - table = 'Datum' + table = "Datum" for _ in range(6): - table += ' ' - table += '| Omschrijving' + table += " " + table += "| Omschrijving" for _ in range(14): - table += ' ' - table += '| Verandering' + table += " " + table += "| Verandering" for _ in range(2): - table += ' ' + table += " " while len(entries) > 0: - table += '\n' + table += "\n" # Find next entry in order min_entry_index = -1 @@ -187,16 +184,13 @@ def format_entries(currency, locale, entries): if entry.date < min_entry.date: min_entry_index = i continue - if ( - entry.date == min_entry.date and - entry.change < min_entry.change - ): + if entry.date == min_entry.date and entry.change < min_entry.change: min_entry_index = i continue if ( - entry.date == min_entry.date and - entry.change == min_entry.change and - entry.description < min_entry.description + entry.date == min_entry.date + and entry.change == min_entry.change + and entry.description < min_entry.description ): min_entry_index = i continue @@ -207,93 +201,92 @@ def format_entries(currency, locale, entries): day = entry.date.day day = str(day) if len(day) < 2: - day = '0' + day + day = "0" + day date_str = day - date_str += '-' + date_str += "-" month = entry.date.month month = str(month) if len(month) < 2: - month = '0' + month + month = "0" + month date_str += month - date_str += '-' + date_str += "-" year = entry.date.year year = str(year) while len(year) < 4: - year = '0' + year + year = "0" + year date_str += year table += date_str - table += ' | ' + table += " | " # Write entry description to table # Truncate if necessary if len(entry.description) > 25: for i in range(22): table += entry.description[i] - table += '...' + table += "..." else: for i in range(25): if len(entry.description) > i: table += entry.description[i] else: - table += ' ' - table += ' | ' + table += " " + table += " | " # Write entry change to table - if currency == 'USD': - change_str = '$ ' + if currency == "USD": + change_str = "$ " if entry.change < 0: - change_str += '-' + change_str += "-" change_dollar = abs(int(entry.change / 100.0)) dollar_parts = [] while change_dollar > 0: dollar_parts.insert(0, str(change_dollar % 1000)) change_dollar = change_dollar // 1000 if len(dollar_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += dollar_parts[0] dollar_parts.pop(0) if len(dollar_parts) == 0: break - change_str += '.' - change_str += ',' + change_str += "." + change_str += "," change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str - elif currency == 'EUR': - change_str = u'€ ' + elif currency == "EUR": + change_str = "€ " if entry.change < 0: - change_str += '-' + change_str += "-" change_euro = abs(int(entry.change / 100.0)) euro_parts = [] while change_euro > 0: euro_parts.insert(0, str(change_euro % 1000)) change_euro = change_euro // 1000 if len(euro_parts) == 0: - change_str += '0' + change_str += "0" else: while True: change_str += euro_parts[0] euro_parts.pop(0) if len(euro_parts) == 0: break - change_str += '.' - change_str += ',' + change_str += "." + change_str += "," change_cents = abs(entry.change) % 100 change_cents = str(change_cents) if len(change_cents) < 2: - change_cents = '0' + change_cents + change_cents = "0" + change_cents change_str += change_cents - change_str += ' ' + change_str += " " while len(change_str) < 13: - change_str = ' ' + change_str + change_str = " " + change_str table += change_str return table - diff --git a/evaluator/datasets/polyglot_py_unpatched/ledger/solution.py b/evaluator/datasets/polyglot_py_unpatched/ledger/solution.py index 46e8e3c9f..85aac97d1 100644 --- a/evaluator/datasets/polyglot_py_unpatched/ledger/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/ledger/solution.py @@ -1,39 +1,39 @@ # -*- coding: utf-8 -*- from datetime import datetime -ROW_FMT = '{{:<{1}}} | {{:<{2}}} | {{:{0}{3}}}' +ROW_FMT = "{{:<{1}}} | {{:<{2}}} | {{:{0}{3}}}" def truncate(s, length=25): if len(s) <= length: return s - return s[:length - 3] + '...' + return s[: length - 3] + "..." class LCInfo: def __init__(self, locale, currency, columns): self.columns = columns - if locale == 'en_US': - headers = ['Date', 'Description', 'Change'] - self.datefmt = '{0.month:02}/{0.day:02}/{0.year:04}' - self.cur_fmt = '{}{}{}{}' - self.lead_neg = '(' - self.trail_neg = ')' - self.thousands = ',' - self.decimal = '.' - elif locale == 'nl_NL': - headers = ['Datum', 'Omschrijving', 'Verandering'] - self.datefmt = '{0.day:02}-{0.month:02}-{0.year:04}' - self.cur_fmt = '{1} {0}{2}{3}' - self.lead_neg = '-' - self.trail_neg = ' ' - self.thousands = '.' - self.decimal = ',' - fmt = ROW_FMT.format('<', *columns) + if locale == "en_US": + headers = ["Date", "Description", "Change"] + self.datefmt = "{0.month:02}/{0.day:02}/{0.year:04}" + self.cur_fmt = "{}{}{}{}" + self.lead_neg = "(" + self.trail_neg = ")" + self.thousands = "," + self.decimal = "." + elif locale == "nl_NL": + headers = ["Datum", "Omschrijving", "Verandering"] + self.datefmt = "{0.day:02}-{0.month:02}-{0.year:04}" + self.cur_fmt = "{1} {0}{2}{3}" + self.lead_neg = "-" + self.trail_neg = " " + self.thousands = "." + self.decimal = "," + fmt = ROW_FMT.format("<", *columns) self.headers = fmt.format(*headers) self.cur_symbol = { - 'USD': '$', - 'EUR': '€', + "USD": "$", + "EUR": "€", }.get(currency) def number(self, n): @@ -42,23 +42,23 @@ def number(self, n): while n_int > 0: n_int, idx = divmod(n_int, 1000) n_int_parts.insert(0, str(idx)) - return '{}{}{:02}'.format( - self.thousands.join(n_int_parts) or '0', + return "{}{}{:02}".format( + self.thousands.join(n_int_parts) or "0", self.decimal, n_float, ) def currency(self, change): return self.cur_fmt.format( - self.lead_neg if change < 0 else '', + self.lead_neg if change < 0 else "", self.cur_symbol, self.number(change), - self.trail_neg if change < 0 else ' ', + self.trail_neg if change < 0 else " ", ) def entry(self, entry): date, change, desc = entry - fmt = ROW_FMT.format('>', *self.columns) + fmt = ROW_FMT.format(">", *self.columns) return fmt.format( self.datefmt.format(date), truncate(desc), @@ -68,15 +68,11 @@ def entry(self, entry): def table(self, entries): lines = [self.headers] lines.extend(map(self.entry, sorted(entries))) - return '\n'.join(lines) + return "\n".join(lines) def create_entry(date, description, change): - return ( - datetime.strptime(date, '%Y-%m-%d'), - change, - description - ) + return (datetime.strptime(date, "%Y-%m-%d"), change, description) def format_entries(currency, locale, entries): diff --git a/evaluator/datasets/polyglot_py_unpatched/ledger/tests.py b/evaluator/datasets/polyglot_py_unpatched/ledger/tests.py index e82758a64..d264bc4c5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/ledger/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/ledger/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - format_entries, create_entry, + format_entries, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/list-ops/tests.py b/evaluator/datasets/polyglot_py_unpatched/list-ops/tests.py index 8677b524e..4badfd136 100644 --- a/evaluator/datasets/polyglot_py_unpatched/list-ops/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/list-ops/tests.py @@ -11,7 +11,11 @@ foldr, length, reverse, +) +from main import ( filter as list_ops_filter, +) +from main import ( map as list_ops_map, ) @@ -84,17 +88,13 @@ def test_reverse_non_empty_list(self): self.assertEqual(reverse([1, 3, 5, 7]), [7, 5, 3, 1]) def test_reverse_list_of_lists_is_not_flattened(self): - self.assertEqual( - reverse([[1, 2], [3], [], [4, 5, 6]]), [[4, 5, 6], [], [3], [1, 2]] - ) + self.assertEqual(reverse([[1, 2], [3], [], [4, 5, 6]]), [[4, 5, 6], [], [3], [1, 2]]) # Additional tests for this track def test_foldr_foldr_add_string(self): self.assertEqual( - foldr( - lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!" - ), + foldr(lambda acc, el: el + acc, ["e", "x", "e", "r", "c", "i", "s", "m"], "!"), "exercism!", ) diff --git a/evaluator/datasets/polyglot_py_unpatched/luhn/solution.py b/evaluator/datasets/polyglot_py_unpatched/luhn/solution.py index 32a4dbc48..1380d8c93 100644 --- a/evaluator/datasets/polyglot_py_unpatched/luhn/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/luhn/solution.py @@ -2,7 +2,7 @@ class Luhn: def __init__(self, card_num): self.card_num = card_num self.checksum = -1 - digits = card_num.replace(' ', '') + digits = card_num.replace(" ", "") length = len(digits) if digits.isdigit() and length > 1: self.checksum = 0 diff --git a/evaluator/datasets/polyglot_py_unpatched/markdown/main.py b/evaluator/datasets/polyglot_py_unpatched/markdown/main.py index 3c4bd2fa8..2954b00ae 100644 --- a/evaluator/datasets/polyglot_py_unpatched/markdown/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/markdown/main.py @@ -2,76 +2,72 @@ def parse(markdown): - lines = markdown.split('\n') - res = '' + lines = markdown.split("\n") + res = "" in_list = False in_list_append = False for i in lines: - if re.match('###### (.*)', i) is not None: - i = '
' + i[7:] + '
' - elif re.match('##### (.*)', i) is not None: - i = '
' + i[6:] + '
' - elif re.match('#### (.*)', i) is not None: - i = '

' + i[5:] + '

' - elif re.match('### (.*)', i) is not None: - i = '

' + i[4:] + '

' - elif re.match('## (.*)', i) is not None: - i = '

' + i[3:] + '

' - elif re.match('# (.*)', i) is not None: - i = '

' + i[2:] + '

' - m = re.match(r'\* (.*)', i) + if re.match("###### (.*)", i) is not None: + i = "
" + i[7:] + "
" + elif re.match("##### (.*)", i) is not None: + i = "
" + i[6:] + "
" + elif re.match("#### (.*)", i) is not None: + i = "

" + i[5:] + "

" + elif re.match("### (.*)", i) is not None: + i = "

" + i[4:] + "

" + elif re.match("## (.*)", i) is not None: + i = "

" + i[3:] + "

" + elif re.match("# (.*)", i) is not None: + i = "

" + i[2:] + "

" + m = re.match(r"\* (.*)", i) if m: if not in_list: in_list = True is_bold = False is_italic = False curr = m.group(1) - m1 = re.match('(.*)__(.*)__(.*)', curr) + m1 = re.match("(.*)__(.*)__(.*)", curr) if m1: - curr = m1.group(1) + '' + \ - m1.group(2) + '' + m1.group(3) + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) is_bold = True - m1 = re.match('(.*)_(.*)_(.*)', curr) + m1 = re.match("(.*)_(.*)_(.*)", curr) if m1: - curr = m1.group(1) + '' + m1.group(2) + \ - '' + m1.group(3) + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) is_italic = True - i = '
  • ' + curr + '
  • ' + i = "
    • " + curr + "
    • " else: is_bold = False is_italic = False curr = m.group(1) - m1 = re.match('(.*)__(.*)__(.*)', curr) + m1 = re.match("(.*)__(.*)__(.*)", curr) if m1: is_bold = True - m1 = re.match('(.*)_(.*)_(.*)', curr) + m1 = re.match("(.*)_(.*)_(.*)", curr) if m1: is_italic = True if is_bold: - curr = m1.group(1) + '' + \ - m1.group(2) + '' + m1.group(3) + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) if is_italic: - curr = m1.group(1) + '' + m1.group(2) + \ - '' + m1.group(3) - i = '
    • ' + curr + '
    • ' + curr = m1.group(1) + "" + m1.group(2) + "" + m1.group(3) + i = "
    • " + curr + "
    • " else: if in_list: in_list_append = True in_list = False - m = re.match('' - m = re.match('(.*)__(.*)__(.*)', i) + i = "

      " + i + "

      " + m = re.match("(.*)__(.*)__(.*)", i) if m: - i = m.group(1) + '' + m.group(2) + '' + m.group(3) - m = re.match('(.*)_(.*)_(.*)', i) + i = m.group(1) + "" + m.group(2) + "" + m.group(3) + m = re.match("(.*)_(.*)_(.*)", i) if m: - i = m.group(1) + '' + m.group(2) + '' + m.group(3) + i = m.group(1) + "" + m.group(2) + "" + m.group(3) if in_list_append: - i = '
    ' + i + i = "
" + i in_list_append = False res += i if in_list: - res += '' + res += "" return res diff --git a/evaluator/datasets/polyglot_py_unpatched/markdown/solution.py b/evaluator/datasets/polyglot_py_unpatched/markdown/solution.py index 1c9d04cf9..282c8cae7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/markdown/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/markdown/solution.py @@ -2,49 +2,47 @@ def parse(markdown): - lines = markdown.split('\n') - html = '' + lines = markdown.split("\n") + html = "" in_list = False in_list_append = False for line in lines: result = parse_line(line, in_list, in_list_append) - html += result['line'] - in_list = result['in_list'] - in_list_append = result['in_list_append'] + html += result["line"] + in_list = result["in_list"] + in_list_append = result["in_list_append"] if in_list: - html += '' + html += "" return html def wrap(line, tag): - return '<{tag}>{line}'.format(line=line, tag=tag) + return "<{tag}>{line}".format(line=line, tag=tag) def check_headers(line): - pattern = '# (.*)' + pattern = "# (.*)" for index in range(6): if re.match(pattern, line): - return wrap(line[(index + 2):], 'h' + str(index + 1)) - pattern = '#' + pattern + return wrap(line[(index + 2) :], "h" + str(index + 1)) + pattern = "#" + pattern return line def check_bold(line): - bold_pattern = '(.*)__(.*)__(.*)' + bold_pattern = "(.*)__(.*)__(.*)" bold_match = re.match(bold_pattern, line) if bold_match: - return bold_match.group(1) + wrap(bold_match.group(2), 'strong')\ - + bold_match.group(3) + return bold_match.group(1) + wrap(bold_match.group(2), "strong") + bold_match.group(3) else: return None def check_italic(line): - italic_pattern = '(.*)_(.*)_(.*)' + italic_pattern = "(.*)_(.*)_(.*)" italic_match = re.match(italic_pattern, line) if italic_match: - return italic_match.group(1) + wrap(italic_match.group(2), 'em')\ - + italic_match.group(3) + return italic_match.group(1) + wrap(italic_match.group(2), "em") + italic_match.group(3) else: return None @@ -52,25 +50,24 @@ def check_italic(line): def parse_line(line, in_list, in_list_append): result = check_headers(line) - list_match = re.match(r'\* (.*)', result) + list_match = re.match(r"\* (.*)", result) if list_match: if not in_list: - result = '
    ' + wrap(list_match.group(1), 'li') + result = "
      " + wrap(list_match.group(1), "li") in_list = True else: - result = wrap(list_match.group(1), 'li') + result = wrap(list_match.group(1), "li") else: if in_list: in_list_append = True in_list = False - if not re.match(')(.*)()(.*)', - r'\1\2

      \3

      \4\5', result) + result = re.sub("(.*)(
    • )(.*)(
    • )(.*)", r"\1\2

      \3

      \4\5", result) while check_bold(result): result = check_bold(result) @@ -78,11 +75,7 @@ def parse_line(line, in_list, in_list_append): result = check_italic(result) if in_list_append: - result = '
    ' + result + result = "
" + result in_list_append = False - return { - 'line': result, - 'in_list': in_list, - 'in_list_append': in_list_append - } + return {"line": result, "in_list": in_list, "in_list_append": in_list_append} diff --git a/evaluator/datasets/polyglot_py_unpatched/markdown/tests.py b/evaluator/datasets/polyglot_py_unpatched/markdown/tests.py index c48d2912b..f39c04d55 100644 --- a/evaluator/datasets/polyglot_py_unpatched/markdown/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/markdown/tests.py @@ -11,19 +11,13 @@ class MarkdownTest(unittest.TestCase): def test_parses_normal_text_as_a_paragraph(self): - self.assertEqual( - parse("This will be a paragraph"), "

This will be a paragraph

" - ) + self.assertEqual(parse("This will be a paragraph"), "

This will be a paragraph

") def test_parsing_italics(self): - self.assertEqual( - parse("_This will be italic_"), "

This will be italic

" - ) + self.assertEqual(parse("_This will be italic_"), "

This will be italic

") def test_parsing_bold_text(self): - self.assertEqual( - parse("__This will be bold__"), "

This will be bold

" - ) + self.assertEqual(parse("__This will be bold__"), "

This will be bold

") def test_mixed_normal_italics_and_bold_text(self): self.assertEqual( @@ -41,19 +35,13 @@ def test_with_h3_header_level(self): self.assertEqual(parse("### This will be an h3"), "

This will be an h3

") def test_with_h4_header_level(self): - self.assertEqual( - parse("#### This will be an h4"), "

This will be an h4

" - ) + self.assertEqual(parse("#### This will be an h4"), "

This will be an h4

") def test_with_h5_header_level(self): - self.assertEqual( - parse("##### This will be an h5"), "
This will be an h5
" - ) + self.assertEqual(parse("##### This will be an h5"), "
This will be an h5
") def test_with_h6_header_level(self): - self.assertEqual( - parse("###### This will be an h6"), "
This will be an h6
" - ) + self.assertEqual(parse("###### This will be an h6"), "
This will be an h6
") def test_h7_header_level_is_a_paragraph(self): self.assertEqual( @@ -62,9 +50,7 @@ def test_h7_header_level_is_a_paragraph(self): ) def test_unordered_lists(self): - self.assertEqual( - parse("* Item 1\n* Item 2"), "
  • Item 1
  • Item 2
" - ) + self.assertEqual(parse("* Item 1\n* Item 2"), "
  • Item 1
  • Item 2
") def test_with_a_little_bit_of_everything(self): self.assertEqual( diff --git a/evaluator/datasets/polyglot_py_unpatched/matching-brackets/solution.py b/evaluator/datasets/polyglot_py_unpatched/matching-brackets/solution.py index b37f13207..e219e9d8a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/matching-brackets/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/matching-brackets/solution.py @@ -1,5 +1,5 @@ def is_paired(input_string): - counterparts = {')': '(', '}': '{', ']': '['} + counterparts = {")": "(", "}": "{", "]": "["} stack = [] for char in input_string: diff --git a/evaluator/datasets/polyglot_py_unpatched/matching-brackets/tests.py b/evaluator/datasets/polyglot_py_unpatched/matching-brackets/tests.py index f43715bc3..444778bf2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/matching-brackets/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/matching-brackets/tests.py @@ -69,8 +69,6 @@ def test_math_expression(self): def test_complex_latex_expression(self): self.assertEqual( - is_paired( - "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)" - ), + is_paired("\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)"), True, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/matrix/solution.py b/evaluator/datasets/polyglot_py_unpatched/matrix/solution.py index 3c89844be..de82ed20f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/matrix/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/matrix/solution.py @@ -1,7 +1,6 @@ class Matrix: def __init__(self, matrix_string): - self.rows = [[int(number) for number in row.split()] - for row in matrix_string.split('\n')] + self.rows = [[int(number) for number in row.split()] for row in matrix_string.split("\n")] self.columns = [list(tup) for tup in zip(*self.rows)] def row(self, index): diff --git a/evaluator/datasets/polyglot_py_unpatched/meetup/main.py b/evaluator/datasets/polyglot_py_unpatched/meetup/main.py index 10f6bcaac..3aaf8077a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/meetup/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/meetup/main.py @@ -5,6 +5,7 @@ class MeetupDayException(ValueError): message: explanation of the error. """ + def __init__(self): pass diff --git a/evaluator/datasets/polyglot_py_unpatched/meetup/solution.py b/evaluator/datasets/polyglot_py_unpatched/meetup/solution.py index d54710748..43766ca7d 100644 --- a/evaluator/datasets/polyglot_py_unpatched/meetup/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/meetup/solution.py @@ -2,25 +2,27 @@ def meetup(year, month, week, day_of_week): - candidates = [date - for date in Calendar().itermonthdates(year, month) - if date.month == month - if date.strftime('%A') == day_of_week] + candidates = [ + date + for date in Calendar().itermonthdates(year, month) + if date.month == month + if date.strftime("%A") == day_of_week + ] return _choice(week)(candidates) def _choice(week): - if week == 'teenth': - return lambda dates: next(date for date in dates if - 13 <= date.day <= 19) + if week == "teenth": + return lambda dates: next(date for date in dates if 13 <= date.day <= 19) - ordinals = ('first', 'second', 'third', 'fourth', 'fifth', 'sixth') - day = -1 if (week == 'last') else (ordinals.index(week)) + ordinals = ("first", "second", "third", "fourth", "fifth", "sixth") + day = -1 if (week == "last") else (ordinals.index(week)) def _func(dates): if day < len(dates): return dates[day] - raise MeetupDayException('That day does not exist.') + raise MeetupDayException("That day does not exist.") + return _func @@ -30,5 +32,6 @@ class MeetupDayException(ValueError): message: explanation of the error. """ + def __init__(self, message): self.message = message diff --git a/evaluator/datasets/polyglot_py_unpatched/meetup/tests.py b/evaluator/datasets/polyglot_py_unpatched/meetup/tests.py index 5e8960b8b..14b420191 100644 --- a/evaluator/datasets/polyglot_py_unpatched/meetup/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/meetup/tests.py @@ -2,12 +2,12 @@ # https://github.com/exercism/problem-specifications/tree/main/exercises/meetup/canonical-data.json # File last updated on 2023-07-19 -from datetime import date import unittest +from datetime import date from main import ( - meetup, MeetupDayException, + meetup, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/minesweeper/solution.py b/evaluator/datasets/polyglot_py_unpatched/minesweeper/solution.py index 4ebbc8413..33c3228ea 100644 --- a/evaluator/datasets/polyglot_py_unpatched/minesweeper/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/minesweeper/solution.py @@ -8,33 +8,33 @@ def annotate(minefield): for index1 in range(col_len): for index2 in range(row_len): - if board[index1][index2] != ' ': + if board[index1][index2] != " ": continue low = max(index2 - 1, 0) high = min(index2 + 2, row_len + 2) - counts = minefield[index1][low:high].count('*') + counts = minefield[index1][low:high].count("*") if index1 > 0: - counts += minefield[index1 - 1][low:high].count('*') + counts += minefield[index1 - 1][low:high].count("*") if index1 < col_len - 1: - counts += minefield[index1 + 1][low:high].count('*') + counts += minefield[index1 + 1][low:high].count("*") if counts == 0: continue board[index1][index2] = str(counts) - return [''.join(row) for row in board] + return ["".join(row) for row in board] def verify_board(minefield): # Rows with different lengths row_len = len(minefield[0]) if not all(len(row) == row_len for row in minefield): - raise ValueError('The board is invalid with current input.') + raise ValueError("The board is invalid with current input.") # Unknown character in board character_set = set() for row in minefield: character_set.update(row) - if character_set - set(' *'): - raise ValueError('The board is invalid with current input.') + if character_set - set(" *"): + raise ValueError("The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py_unpatched/minesweeper/tests.py b/evaluator/datasets/polyglot_py_unpatched/minesweeper/tests.py index 75e46a32d..5334da7d1 100644 --- a/evaluator/datasets/polyglot_py_unpatched/minesweeper/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/minesweeper/tests.py @@ -63,14 +63,10 @@ def test_different_len(self): with self.assertRaises(ValueError) as err: annotate([" ", "* ", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") def test_invalid_char(self): with self.assertRaises(ValueError) as err: annotate(["X * "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "The board is invalid with current input." - ) + self.assertEqual(err.exception.args[0], "The board is invalid with current input.") diff --git a/evaluator/datasets/polyglot_py_unpatched/nth-prime/solution.py b/evaluator/datasets/polyglot_py_unpatched/nth-prime/solution.py index 73517c4b3..1f6c10df6 100644 --- a/evaluator/datasets/polyglot_py_unpatched/nth-prime/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/nth-prime/solution.py @@ -4,7 +4,7 @@ def prime(number): if number < 1: - raise ValueError('there is no zeroth prime') + raise ValueError("there is no zeroth prime") known = [] candidates = prime_candidates() diff --git a/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/main.py b/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/main.py index 5e20881f1..5d753d548 100644 --- a/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/main.py @@ -1,3 +1,2 @@ def convert(input_grid): pass - diff --git a/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/solution.py b/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/solution.py index bc926487e..1dfbd4dea 100644 --- a/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/solution.py @@ -3,41 +3,43 @@ def split_ocr(ocr): - return [[ocr[idx][NUM_COLS * jam:NUM_COLS * (jam + 1)] for idx in range(NUM_ROWS)] - for jam in range(len(ocr[0]) // NUM_COLS)] + return [ + [ocr[idx][NUM_COLS * jam : NUM_COLS * (jam + 1)] for idx in range(NUM_ROWS)] + for jam in range(len(ocr[0]) // NUM_COLS) + ] -ALL = [' _ _ _ _ _ _ _ _ ', - ' | _| _||_||_ |_ ||_||_|| |', - ' ||_ _| | _||_| ||_| _||_|', - ' '] +ALL = [ + " _ _ _ _ _ _ _ _ ", + " | _| _||_||_ |_ ||_||_|| |", + " ||_ _| | _||_| ||_| _||_|", + " ", +] OCR_LIST = split_ocr(ALL) OCR_LIST = [OCR_LIST[-1]] + OCR_LIST[:9] def convert(input_grid): - split_indices = (list(range(0, len(input_grid), NUM_ROWS)) + - [len(input_grid)]) + split_indices = list(range(0, len(input_grid), NUM_ROWS)) + [len(input_grid)] - lines = [input_grid[start:end] - for start, end in zip(split_indices[:-1], split_indices[1:])] + lines = [input_grid[start:end] for start, end in zip(split_indices[:-1], split_indices[1:])] - return ','.join(convert_one_line(line) for line in lines) + return ",".join(convert_one_line(line) for line in lines) def convert_one_line(input_grid): if len(input_grid) != NUM_ROWS: - raise ValueError('Number of input lines is not a multiple of four') + raise ValueError("Number of input lines is not a multiple of four") if len(input_grid[0]) % NUM_COLS: - raise ValueError('Number of input columns is not a multiple of three') + raise ValueError("Number of input columns is not a multiple of three") numbers = split_ocr(input_grid) - digits = '' + digits = "" for num in numbers: try: digits += str(OCR_LIST.index(num)) except ValueError: - digits += '?' + digits += "?" return digits diff --git a/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/tests.py b/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/tests.py index ee5df73b6..a934da2f2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/ocr-numbers/tests.py @@ -25,9 +25,7 @@ def test_input_with_a_number_of_lines_that_is_not_a_multiple_of_four_raises_an_e with self.assertRaises(ValueError) as err: convert([" _ ", "| |", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "Number of input lines is not a multiple of four" - ) + self.assertEqual(err.exception.args[0], "Number of input lines is not a multiple of four") def test_input_with_a_number_of_columns_that_is_not_a_multiple_of_three_raises_an_error( self, @@ -35,9 +33,7 @@ def test_input_with_a_number_of_columns_that_is_not_a_multiple_of_three_raises_a with self.assertRaises(ValueError) as err: convert([" ", " |", " |", " "]) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "Number of input columns is not a multiple of three" - ) + self.assertEqual(err.exception.args[0], "Number of input columns is not a multiple of three") def test_recognizes_110101100(self): self.assertEqual( diff --git a/evaluator/datasets/polyglot_py_unpatched/octal/solution.py b/evaluator/datasets/polyglot_py_unpatched/octal/solution.py index 86000ee4d..9da7b560d 100644 --- a/evaluator/datasets/polyglot_py_unpatched/octal/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/octal/solution.py @@ -1,11 +1,10 @@ def parse_octal(digits): digits = _validate_octal(digits) - return sum(int(digit) * 8 ** idx - for (idx, digit) in enumerate(reversed(digits))) + return sum(int(digit) * 8**idx for (idx, digit) in enumerate(reversed(digits))) def _validate_octal(digits): for digit in digits: - if not '0' <= digit < '8': + if not "0" <= digit < "8": raise ValueError("Invalid octal digit: " + digit) return digits diff --git a/evaluator/datasets/polyglot_py_unpatched/octal/tests.py b/evaluator/datasets/polyglot_py_unpatched/octal/tests.py index f53a4913b..4514d8fb1 100644 --- a/evaluator/datasets/polyglot_py_unpatched/octal/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/octal/tests.py @@ -4,6 +4,7 @@ If the string supplied to parse_octal cannot be parsed as an octal number your program should raise a ValueError with a meaningful error message. """ + import unittest from main import parse_octal @@ -48,5 +49,5 @@ def assertRaisesWithMessage(self, exception): return self.assertRaisesRegex(exception, r".+") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/paasio/tests.py b/evaluator/datasets/polyglot_py_unpatched/paasio/tests.py index e2df06bab..383c25e1c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/paasio/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/paasio/tests.py @@ -1,13 +1,12 @@ import errno -import os -import unittest import inspect import io -from unittest.mock import ANY, call, NonCallableMagicMock, patch +import os +import unittest +from unittest.mock import ANY, NonCallableMagicMock, call, patch from main import MeteredFile, MeteredSocket - ZEN = b"""Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. @@ -89,9 +88,7 @@ def recv(self, bufsize, flags=0): if bufsize is None: raise TypeError("'NoneType' object cannot be interpreted as an integer") if not isinstance(flags, int): - raise TypeError( - "an integer is required (got type {})".format(type(flags).__name__) - ) + raise TypeError("an integer is required (got type {})".format(type(flags).__name__)) self.flags = flags if self.__exception is not None: raise self.__exception @@ -104,9 +101,7 @@ def send(self, data, flags=0): if self.__closed: raise OSError(errno.EBADF, os.strerror(errno.EBADF)) if not isinstance(flags, int): - raise TypeError( - "an integer is required (got type {})".format(type(flags).__name__) - ) + raise TypeError("an integer is required (got type {})".format(type(flags).__name__)) self.flags = flags if self.__chunk is None: return self._sender.write(data) @@ -133,14 +128,13 @@ def __call__(self, *args, **kwargs): return self.mock_object def __repr__(self): - return "".format( - hex(id(self)), self.mock_object - ) + return "".format(hex(id(self)), self.mock_object) mock_object = None init_called = 0 initialized = False + class PaasioTest(unittest.TestCase): def test_meteredsocket_context_manager(self): wrapped = MockSock() @@ -282,10 +276,7 @@ def test_meteredsocket_bufsize_required(self): with self.assertRaisesRegex(TypeError, "^'NoneType'.+integer$"): with MeteredSocket(mock) as socket: socket.recv(None) - self.assertTrue( - call(None) in mock.recv.mock_calls - or call(None, ANY) in mock.recv.mock_calls - ) + self.assertTrue(call(None) in mock.recv.mock_calls or call(None, ANY) in mock.recv.mock_calls) def test_meteredsocket_flags_support(self): mock = NonCallableMagicMock(wraps=MockSock(), autospec=True) @@ -408,9 +399,7 @@ def test_meteredfile_iteration(self, super_mock): for line in file: actual_reads += line self.assertLess(0, mock.readline.call_count, "File's readline not called") - self.assertGreater( - 50, mock.readline.call_count, "Possible infinte loop detected" - ) + self.assertGreater(50, mock.readline.call_count, "Possible infinte loop detected") self.assertEqual(file.read_ops, mock.readline.call_count) self.assertFalse(mock.__iter__.called) self.assertEqual(len(ZEN), file.read_bytes) diff --git a/evaluator/datasets/polyglot_py_unpatched/palindrome-products/solution.py b/evaluator/datasets/polyglot_py_unpatched/palindrome-products/solution.py index f656ff185..6c91203d3 100644 --- a/evaluator/datasets/polyglot_py_unpatched/palindrome-products/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/palindrome-products/solution.py @@ -1,24 +1,23 @@ from itertools import chain -from math import log10, floor, ceil +from math import ceil, floor, log10 def largest(min_factor, max_factor): - return get_extreme_palindrome_with_factors(max_factor, min_factor, 'largest') + return get_extreme_palindrome_with_factors(max_factor, min_factor, "largest") def smallest(max_factor, min_factor): - return get_extreme_palindrome_with_factors(max_factor, min_factor, 'smallest') + return get_extreme_palindrome_with_factors(max_factor, min_factor, "smallest") def get_extreme_palindrome_with_factors(max_factor, min_factor, extreme): - palindromes_found = palindromes(max_factor, min_factor, reverse=(extreme == 'largest')) + palindromes_found = palindromes(max_factor, min_factor, reverse=(extreme == "largest")) factor_pairs = None for palindrome in palindromes_found: - factor_pairs = ((factor, palindrome // factor) - for factor in range(min_factor, max_factor + 1) - if palindrome % factor == 0) - factor_pairs = list(pair for pair in factor_pairs - if min_factor <= pair[1] <= max_factor) + factor_pairs = ( + (factor, palindrome // factor) for factor in range(min_factor, max_factor + 1) if palindrome % factor == 0 + ) + factor_pairs = list(pair for pair in factor_pairs if min_factor <= pair[1] <= max_factor) if len(factor_pairs) > 0: break @@ -32,7 +31,7 @@ def reverse_num(number): reversed_nums = 0 while number > 0: reversed_nums *= 10 - reversed_nums += (number % 10) + reversed_nums += number % 10 number //= 10 return reversed_nums @@ -49,10 +48,10 @@ def palindromes(max_factor, min_factor, reverse=False): most of the palindromes just to find the one it needs. """ if max_factor < min_factor: - raise ValueError('min must be <= max') + raise ValueError("min must be <= max") - minimum = min_factor ** 2 - maximum = max_factor ** 2 + minimum = min_factor**2 + maximum = max_factor**2 def gen_palindromes_of_length(digit_count, reverse=reverse): """Generates all palindromes with `nd` number of digits that are @@ -60,19 +59,15 @@ def gen_palindromes_of_length(digit_count, reverse=reverse): Again, if `reverse` is True, the palindromes are generated in reverse order. """ - even_nd = (digit_count % 2 == 0) + even_nd = digit_count % 2 == 0 - min_left_half = max(10 ** (int(ceil(digit_count / 2)) - 1), - minimum // (10 ** (digit_count // 2))) - max_left_half = min((10 ** int(ceil(digit_count / 2))) - 1, - maximum // (10 ** (digit_count // 2))) + min_left_half = max(10 ** (int(ceil(digit_count / 2)) - 1), minimum // (10 ** (digit_count // 2))) + max_left_half = min((10 ** int(ceil(digit_count / 2))) - 1, maximum // (10 ** (digit_count // 2))) current_left_half = min_left_half if not reverse else max_left_half def make_palindrome(left_half, even_nd=False): - right_half = (reverse_num(left_half) - if even_nd - else reverse_num(left_half // 10)) + right_half = reverse_num(left_half) if even_nd else reverse_num(left_half // 10) return (left_half * (10 ** (digit_count // 2))) + right_half if not reverse: @@ -101,8 +96,6 @@ def make_palindrome(left_half, even_nd=False): min_nd = num_digits(minimum) max_nd = num_digits(maximum) - lengths = (range(min_nd, max_nd + 1) - if not reverse - else range(max_nd, min_nd - 1, -1)) + lengths = range(min_nd, max_nd + 1) if not reverse else range(max_nd, min_nd - 1, -1) return chain(*map(gen_palindromes_of_length, lengths)) diff --git a/evaluator/datasets/polyglot_py_unpatched/pangram/tests.py b/evaluator/datasets/polyglot_py_unpatched/pangram/tests.py index 316ff88da..ae8e0df5d 100644 --- a/evaluator/datasets/polyglot_py_unpatched/pangram/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/pangram/tests.py @@ -32,9 +32,7 @@ def test_with_underscores(self): self.assertIs(is_pangram("the_quick_brown_fox_jumps_over_the_lazy_dog"), True) def test_with_numbers(self): - self.assertIs( - is_pangram("the 1 quick brown fox jumps over the 2 lazy dogs"), True - ) + self.assertIs(is_pangram("the 1 quick brown fox jumps over the 2 lazy dogs"), True) def test_missing_letters_replaced_by_numbers(self): self.assertIs(is_pangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"), False) diff --git a/evaluator/datasets/polyglot_py_unpatched/pascals-triangle/tests.py b/evaluator/datasets/polyglot_py_unpatched/pascals-triangle/tests.py index 0e9f238cc..e8a5434af 100644 --- a/evaluator/datasets/polyglot_py_unpatched/pascals-triangle/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/pascals-triangle/tests.py @@ -59,6 +59,4 @@ def test_solution_is_recursive(self): with self.assertRaises(RecursionError) as err: rows(sys.getrecursionlimit() + 10) self.assertEqual(type(err.exception), RecursionError) - self.assertEqual( - err.exception.args[0][:32], "maximum recursion depth exceeded" - ) + self.assertEqual(err.exception.args[0][:32], "maximum recursion depth exceeded") diff --git a/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/main.py b/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/main.py index eb093dd6c..1b289ed0c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/main.py @@ -1,5 +1,5 @@ def classify(number): - """ A perfect number equals the sum of its positive divisors. + """A perfect number equals the sum of its positive divisors. :param number: int a positive integer :return: str the classification of the input integer diff --git a/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/solution.py b/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/solution.py index 3a3494817..fff76c259 100644 --- a/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/perfect-numbers/solution.py @@ -1,5 +1,6 @@ import math + def divisor_generator(number): """Returns an unordered list of divisors for n (1 < number). @@ -15,20 +16,20 @@ def divisor_generator(number): def classify(number): - """ A perfect number equals the sum of its positive divisors. + """A perfect number equals the sum of its positive divisors. :param number: int a positive integer :return: str the classification of the input integer """ if number <= 0: - raise ValueError('Classification is only possible for positive integers.') + raise ValueError("Classification is only possible for positive integers.") aliquot_sum = sum(divisor_generator(number)) + (1 if number > 1 else 0) if aliquot_sum < number: - return 'deficient' + return "deficient" elif aliquot_sum == number: - return 'perfect' + return "perfect" else: - return 'abundant' + return "abundant" diff --git a/evaluator/datasets/polyglot_py_unpatched/phone-number/solution.py b/evaluator/datasets/polyglot_py_unpatched/phone-number/solution.py index d23102a01..58b59f39b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/phone-number/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/phone-number/solution.py @@ -10,44 +10,44 @@ def __init__(self, number): self.subscriber_number = self.number[-4:] def pretty(self): - return f'({self.area_code})-{self.exchange_code}-{self.subscriber_number}' + return f"({self.area_code})-{self.exchange_code}-{self.subscriber_number}" def _clean(self, number): - preprocess = re.sub(r'[() +-.]', '', number) + preprocess = re.sub(r"[() +-.]", "", number) if any(item for item in preprocess if item.isalpha()): - raise ValueError('letters not permitted') + raise ValueError("letters not permitted") if any(item for item in preprocess if item in punctuation): - raise ValueError('punctuations not permitted') + raise ValueError("punctuations not permitted") return self._normalize(preprocess) def _normalize(self, number): if len(number) < 10: - raise ValueError('must not be fewer than 10 digits') + raise ValueError("must not be fewer than 10 digits") if len(number) > 11: - raise ValueError('must not be greater than 11 digits') - - if len(number) == 10 or len(number) == 11 and number.startswith('1'): - if number[-10] == '0': - raise ValueError('area code cannot start with zero') - elif number[-10] == '1': - raise ValueError('area code cannot start with one') - elif number[-7] == '0': - raise ValueError('exchange code cannot start with zero') - elif number[-7] == '1': - raise ValueError('exchange code cannot start with one') + raise ValueError("must not be greater than 11 digits") + + if len(number) == 10 or len(number) == 11 and number.startswith("1"): + if number[-10] == "0": + raise ValueError("area code cannot start with zero") + elif number[-10] == "1": + raise ValueError("area code cannot start with one") + elif number[-7] == "0": + raise ValueError("exchange code cannot start with zero") + elif number[-7] == "1": + raise ValueError("exchange code cannot start with one") else: - valid = number[-10] in '23456789' and number[-7] in '23456789' + valid = number[-10] in "23456789" and number[-7] in "23456789" else: valid = False - if number[0] in '023456789': - raise ValueError('11 digits must start with 1') + if number[0] in "023456789": + raise ValueError("11 digits must start with 1") if valid: return number[-10:] - return None # [Pylint]: R1710; + return None # [Pylint]: R1710; diff --git a/evaluator/datasets/polyglot_py_unpatched/pig-latin/solution.py b/evaluator/datasets/polyglot_py_unpatched/pig-latin/solution.py index f4b5ca575..4461199f6 100644 --- a/evaluator/datasets/polyglot_py_unpatched/pig-latin/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/pig-latin/solution.py @@ -1,8 +1,7 @@ import re - -re_cons = re.compile('^([^aeiou]?qu|[^aeiouy]+|y(?=[aeiou]))([a-z]*)') -re_vowel = re.compile('^([aeiou]|y[^aeiou]|xr)[a-z]*') +re_cons = re.compile("^([^aeiou]?qu|[^aeiouy]+|y(?=[aeiou]))([a-z]*)") +re_vowel = re.compile("^([aeiou]|y[^aeiou]|xr)[a-z]*") def split_initial_consonant_sound(word): @@ -17,8 +16,8 @@ def translate(text): words = [] for word in text.split(): if starts_with_vowel_sound(word): - words.append(word + 'ay') + words.append(word + "ay") else: head, tail = split_initial_consonant_sound(word) - words.append(tail + head + 'ay') - return ' '.join(words) + words.append(tail + head + "ay") + return " ".join(words) diff --git a/evaluator/datasets/polyglot_py_unpatched/point-mutations/tests.py b/evaluator/datasets/polyglot_py_unpatched/point-mutations/tests.py index 4c26d554e..31c62e5e2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/point-mutations/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/point-mutations/tests.py @@ -5,32 +5,29 @@ class PointMutationsTest(unittest.TestCase): def test_no_difference_between_empty_strands(self): - self.assertEqual(hamming_distance('', ''), 0) + self.assertEqual(hamming_distance("", ""), 0) def test_no_difference_between_identical_strands(self): - self.assertEqual(hamming_distance('GGACTGA', 'GGACTGA'), 0) + self.assertEqual(hamming_distance("GGACTGA", "GGACTGA"), 0) def test_complete_hamming_distance_in_small_strand(self): - self.assertEqual(hamming_distance('ACT', 'GGA'), 3) + self.assertEqual(hamming_distance("ACT", "GGA"), 3) def test_hamming_distance_in_off_by_one_strand(self): - self.assertEqual( - hamming_distance('GGACGGATTCTGACCTGGACTAATTTTGGGG', - 'AGGACGGATTCTGACCTGGACTAATTTTGGGG'), 19) + self.assertEqual(hamming_distance("GGACGGATTCTGACCTGGACTAATTTTGGGG", "AGGACGGATTCTGACCTGGACTAATTTTGGGG"), 19) def test_small_hamming_distance_in_middle_somewhere(self): - self.assertEqual(hamming_distance('GGACG', 'GGTCG'), 1) + self.assertEqual(hamming_distance("GGACG", "GGTCG"), 1) def test_larger_distance(self): - self.assertEqual(hamming_distance('ACCAGGG', 'ACTATGG'), 2) + self.assertEqual(hamming_distance("ACCAGGG", "ACTATGG"), 2) def test_ignores_extra_length_on_other_strand_when_longer(self): - self.assertEqual(hamming_distance('AAACTAGGGG', 'AGGCTAGCGGTAGGAC'), 3) + self.assertEqual(hamming_distance("AAACTAGGGG", "AGGCTAGCGGTAGGAC"), 3) def test_ignores_extra_length_on_original_strand_when_longer(self): - self.assertEqual( - hamming_distance('GACTACGGACAGGGTAGGGAAT', 'GACATCGCACACC'), 5) + self.assertEqual(hamming_distance("GACTACGGACAGGGTAGGGAAT", "GACATCGCACACC"), 5) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/poker/solution.py b/evaluator/datasets/polyglot_py_unpatched/poker/solution.py index e650f143c..e0556061b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/poker/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/poker/solution.py @@ -15,8 +15,8 @@ def allmax(iterable, key=None): def hand_rank(hand): - hand = hand.replace('10', 'T').split() - card_ranks = ['..23456789TJQKA'.index(idx) for idx, _ in hand] + hand = hand.replace("10", "T").split() + card_ranks = ["..23456789TJQKA".index(idx) for idx, _ in hand] groups = [(card_ranks.count(idx), idx) for idx in set(card_ranks)] groups.sort(reverse=True) counts, ranks = zip(*groups) @@ -24,13 +24,25 @@ def hand_rank(hand): ranks = (5, 4, 3, 2, 1) straight = (len(counts) == 5) and (max(ranks) - min(ranks) == 4) flush = len({idx for _, idx in hand}) == 1 - return (9 if counts == (5,) else - 8 if straight and flush else - 7 if counts == (4, 1) else - 6 if counts == (3, 2) else - 5 if flush else - 4 if straight else - 3 if counts == (3, 1, 1) else - 2 if counts == (2, 2, 1) else - 1 if counts == (2, 1, 1, 1) else - 0, ranks) + return ( + 9 + if counts == (5,) + else 8 + if straight and flush + else 7 + if counts == (4, 1) + else 6 + if counts == (3, 2) + else 5 + if flush + else 4 + if straight + else 3 + if counts == (3, 1, 1) + else 2 + if counts == (2, 2, 1) + else 1 + if counts == (2, 1, 1, 1) + else 0, + ranks, + ) diff --git a/evaluator/datasets/polyglot_py_unpatched/poker/tests.py b/evaluator/datasets/polyglot_py_unpatched/poker/tests.py index d36b5e5a9..aae0182bb 100644 --- a/evaluator/datasets/polyglot_py_unpatched/poker/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/poker/tests.py @@ -35,189 +35,121 @@ def test_a_tie_has_multiple_winners(self): def test_multiple_hands_with_the_same_high_cards_tie_compares_next_highest_ranked_down_to_last_card( self, ): - self.assertEqual( - best_hands(["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"]), ["3S 5H 6S 8D 7H"] - ) + self.assertEqual(best_hands(["3S 5H 6S 8D 7H", "2S 5D 6D 8C 7S"]), ["3S 5H 6S 8D 7H"]) def test_winning_high_card_hand_also_has_the_lowest_card(self): - self.assertEqual( - best_hands(["2S 5H 6S 8D 7H", "3S 4D 6D 8C 7S"]), ["2S 5H 6S 8D 7H"] - ) + self.assertEqual(best_hands(["2S 5H 6S 8D 7H", "3S 4D 6D 8C 7S"]), ["2S 5H 6S 8D 7H"]) def test_one_pair_beats_high_card(self): - self.assertEqual( - best_hands(["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"]), ["2S 4H 6S 4D JH"] - ) + self.assertEqual(best_hands(["4S 5H 6C 8D KH", "2S 4H 6S 4D JH"]), ["2S 4H 6S 4D JH"]) def test_highest_pair_wins(self): - self.assertEqual( - best_hands(["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"]), ["2S 4H 6C 4D JD"] - ) + self.assertEqual(best_hands(["4S 2H 6S 2D JH", "2S 4H 6C 4D JD"]), ["2S 4H 6C 4D JD"]) def test_both_hands_have_the_same_pair_high_card_wins(self): - self.assertEqual( - best_hands(["4H 4S AH JC 3D", "4C 4D AS 5D 6C"]), ["4H 4S AH JC 3D"] - ) + self.assertEqual(best_hands(["4H 4S AH JC 3D", "4C 4D AS 5D 6C"]), ["4H 4S AH JC 3D"]) def test_two_pairs_beats_one_pair(self): - self.assertEqual( - best_hands(["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"]), ["4S 5H 4C 8C 5C"] - ) + self.assertEqual(best_hands(["2S 8H 6S 8D JH", "4S 5H 4C 8C 5C"]), ["4S 5H 4C 8C 5C"]) def test_both_hands_have_two_pairs_highest_ranked_pair_wins(self): - self.assertEqual( - best_hands(["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"]), ["2S 8H 2D 8D 3H"] - ) + self.assertEqual(best_hands(["2S 8H 2D 8D 3H", "4S 5H 4C 8S 5D"]), ["2S 8H 2D 8D 3H"]) def test_both_hands_have_two_pairs_with_the_same_highest_ranked_pair_tie_goes_to_low_pair( self, ): - self.assertEqual( - best_hands(["2S QS 2C QD JH", "JD QH JS 8D QC"]), ["JD QH JS 8D QC"] - ) + self.assertEqual(best_hands(["2S QS 2C QD JH", "JD QH JS 8D QC"]), ["JD QH JS 8D QC"]) def test_both_hands_have_two_identically_ranked_pairs_tie_goes_to_remaining_card_kicker( self, ): - self.assertEqual( - best_hands(["JD QH JS 8D QC", "JS QS JC 2D QD"]), ["JD QH JS 8D QC"] - ) + self.assertEqual(best_hands(["JD QH JS 8D QC", "JS QS JC 2D QD"]), ["JD QH JS 8D QC"]) def test_both_hands_have_two_pairs_that_add_to_the_same_value_win_goes_to_highest_pair( self, ): - self.assertEqual( - best_hands(["6S 6H 3S 3H AS", "7H 7S 2H 2S AC"]), ["7H 7S 2H 2S AC"] - ) + self.assertEqual(best_hands(["6S 6H 3S 3H AS", "7H 7S 2H 2S AC"]), ["7H 7S 2H 2S AC"]) def test_two_pairs_first_ranked_by_largest_pair(self): - self.assertEqual( - best_hands(["5C 2S 5S 4H 4C", "6S 2S 6H 7C 2C"]), ["6S 2S 6H 7C 2C"] - ) + self.assertEqual(best_hands(["5C 2S 5S 4H 4C", "6S 2S 6H 7C 2C"]), ["6S 2S 6H 7C 2C"]) def test_three_of_a_kind_beats_two_pair(self): - self.assertEqual( - best_hands(["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"]), ["4S 5H 4C 8S 4H"] - ) + self.assertEqual(best_hands(["2S 8H 2H 8D JH", "4S 5H 4C 8S 4H"]), ["4S 5H 4C 8S 4H"]) def test_both_hands_have_three_of_a_kind_tie_goes_to_highest_ranked_triplet(self): - self.assertEqual( - best_hands(["2S 2H 2C 8D JH", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"] - ) + self.assertEqual(best_hands(["2S 2H 2C 8D JH", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"]) def test_with_multiple_decks_two_players_can_have_same_three_of_a_kind_ties_go_to_highest_remaining_cards( self, ): - self.assertEqual( - best_hands(["5S AH AS 7C AD", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"] - ) + self.assertEqual(best_hands(["5S AH AS 7C AD", "4S AH AS 8C AD"]), ["4S AH AS 8C AD"]) def test_a_straight_beats_three_of_a_kind(self): - self.assertEqual( - best_hands(["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"]), ["3S 4D 2S 6D 5C"] - ) + self.assertEqual(best_hands(["4S 5H 4C 8D 4H", "3S 4D 2S 6D 5C"]), ["3S 4D 2S 6D 5C"]) def test_aces_can_end_a_straight_10_j_q_k_a(self): - self.assertEqual( - best_hands(["4S 5H 4C 8D 4H", "10D JH QS KD AC"]), ["10D JH QS KD AC"] - ) + self.assertEqual(best_hands(["4S 5H 4C 8D 4H", "10D JH QS KD AC"]), ["10D JH QS KD AC"]) def test_aces_can_start_a_straight_a_2_3_4_5(self): - self.assertEqual( - best_hands(["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"]), ["4D AH 3S 2D 5C"] - ) + self.assertEqual(best_hands(["4S 5H 4C 8D 4H", "4D AH 3S 2D 5C"]), ["4D AH 3S 2D 5C"]) def test_aces_cannot_be_in_the_middle_of_a_straight_q_k_a_2_3(self): - self.assertEqual( - best_hands(["2C 3D 7H 5H 2S", "QS KH AC 2D 3S"]), ["2C 3D 7H 5H 2S"] - ) + self.assertEqual(best_hands(["2C 3D 7H 5H 2S", "QS KH AC 2D 3S"]), ["2C 3D 7H 5H 2S"]) def test_both_hands_with_a_straight_tie_goes_to_highest_ranked_card(self): - self.assertEqual( - best_hands(["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"]), ["5S 7H 8S 9D 6H"] - ) + self.assertEqual(best_hands(["4S 6C 7S 8D 5H", "5S 7H 8S 9D 6H"]), ["5S 7H 8S 9D 6H"]) def test_even_though_an_ace_is_usually_high_a_5_high_straight_is_the_lowest_scoring_straight( self, ): - self.assertEqual( - best_hands(["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"]), ["2H 3C 4D 5D 6H"] - ) + self.assertEqual(best_hands(["2H 3C 4D 5D 6H", "4S AH 3S 2D 5H"]), ["2H 3C 4D 5D 6H"]) def test_flush_beats_a_straight(self): - self.assertEqual( - best_hands(["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"]), ["2S 4S 5S 6S 7S"] - ) + self.assertEqual(best_hands(["4C 6H 7D 8D 5H", "2S 4S 5S 6S 7S"]), ["2S 4S 5S 6S 7S"]) def test_both_hands_have_a_flush_tie_goes_to_high_card_down_to_the_last_one_if_necessary( self, ): - self.assertEqual( - best_hands(["2H 7H 8H 9H 6H", "3S 5S 6S 7S 8S"]), ["2H 7H 8H 9H 6H"] - ) + self.assertEqual(best_hands(["2H 7H 8H 9H 6H", "3S 5S 6S 7S 8S"]), ["2H 7H 8H 9H 6H"]) def test_full_house_beats_a_flush(self): - self.assertEqual( - best_hands(["3H 6H 7H 8H 5H", "4S 5H 4C 5D 4H"]), ["4S 5H 4C 5D 4H"] - ) + self.assertEqual(best_hands(["3H 6H 7H 8H 5H", "4S 5H 4C 5D 4H"]), ["4S 5H 4C 5D 4H"]) def test_both_hands_have_a_full_house_tie_goes_to_highest_ranked_triplet(self): - self.assertEqual( - best_hands(["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 8S 8D"] - ) + self.assertEqual(best_hands(["4H 4S 4D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 8S 8D"]) def test_with_multiple_decks_both_hands_have_a_full_house_with_the_same_triplet_tie_goes_to_the_pair( self, ): - self.assertEqual( - best_hands(["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 9S 9D"] - ) + self.assertEqual(best_hands(["5H 5S 5D 9S 9D", "5H 5S 5D 8S 8D"]), ["5H 5S 5D 9S 9D"]) def test_four_of_a_kind_beats_a_full_house(self): - self.assertEqual( - best_hands(["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"]), ["3S 3H 2S 3D 3C"] - ) + self.assertEqual(best_hands(["4S 5H 4D 5D 4H", "3S 3H 2S 3D 3C"]), ["3S 3H 2S 3D 3C"]) def test_both_hands_have_four_of_a_kind_tie_goes_to_high_quad(self): - self.assertEqual( - best_hands(["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"]), ["4S 5H 5S 5D 5C"] - ) + self.assertEqual(best_hands(["2S 2H 2C 8D 2D", "4S 5H 5S 5D 5C"]), ["4S 5H 5S 5D 5C"]) def test_with_multiple_decks_both_hands_with_identical_four_of_a_kind_tie_determined_by_kicker( self, ): - self.assertEqual( - best_hands(["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"]), ["3S 3H 4S 3D 3C"] - ) + self.assertEqual(best_hands(["3S 3H 2S 3D 3C", "3S 3H 4S 3D 3C"]), ["3S 3H 4S 3D 3C"]) def test_straight_flush_beats_four_of_a_kind(self): - self.assertEqual( - best_hands(["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"]), ["7S 8S 9S 6S 10S"] - ) + self.assertEqual(best_hands(["4S 5H 5S 5D 5C", "7S 8S 9S 6S 10S"]), ["7S 8S 9S 6S 10S"]) def test_aces_can_end_a_straight_flush_10_j_q_k_a(self): - self.assertEqual( - best_hands(["KC AH AS AD AC", "10C JC QC KC AC"]), ["10C JC QC KC AC"] - ) + self.assertEqual(best_hands(["KC AH AS AD AC", "10C JC QC KC AC"]), ["10C JC QC KC AC"]) def test_aces_can_start_a_straight_flush_a_2_3_4_5(self): - self.assertEqual( - best_hands(["KS AH AS AD AC", "4H AH 3H 2H 5H"]), ["4H AH 3H 2H 5H"] - ) + self.assertEqual(best_hands(["KS AH AS AD AC", "4H AH 3H 2H 5H"]), ["4H AH 3H 2H 5H"]) def test_aces_cannot_be_in_the_middle_of_a_straight_flush_q_k_a_2_3(self): - self.assertEqual( - best_hands(["2C AC QC 10C KC", "QH KH AH 2H 3H"]), ["2C AC QC 10C KC"] - ) + self.assertEqual(best_hands(["2C AC QC 10C KC", "QH KH AH 2H 3H"]), ["2C AC QC 10C KC"]) def test_both_hands_have_a_straight_flush_tie_goes_to_highest_ranked_card(self): - self.assertEqual( - best_hands(["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"]), ["5S 7S 8S 9S 6S"] - ) + self.assertEqual(best_hands(["4H 6H 7H 8H 5H", "5S 7S 8S 9S 6S"]), ["5S 7S 8S 9S 6S"]) def test_even_though_an_ace_is_usually_high_a_5_high_straight_flush_is_the_lowest_scoring_straight_flush( self, ): - self.assertEqual( - best_hands(["2H 3H 4H 5H 6H", "4D AD 3D 2D 5D"]), ["2H 3H 4H 5H 6H"] - ) + self.assertEqual(best_hands(["2H 3H 4H 5H 6H", "4D AD 3D 2D 5D"]), ["2H 3H 4H 5H 6H"]) diff --git a/evaluator/datasets/polyglot_py_unpatched/pov/solution.py b/evaluator/datasets/polyglot_py_unpatched/pov/solution.py index 9747d985e..839ed593b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/pov/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/pov/solution.py @@ -57,9 +57,7 @@ def from_pov(self, from_node): for child in tree.children: stack.append(child.add(tree.remove(child.label))) - raise ValueError('Tree could not be reoriented') - - + raise ValueError("Tree could not be reoriented") def path_to(self, from_node, to_node): reordered = self.from_pov(from_node) @@ -70,7 +68,7 @@ def path_to(self, from_node, to_node): try: tree = stack.pop() except IndexError as error: - raise ValueError('No path found') from error + raise ValueError("No path found") from error if to_node in tree: path.append(tree.label) stack = tree.children diff --git a/evaluator/datasets/polyglot_py_unpatched/protein-translation/solution.py b/evaluator/datasets/polyglot_py_unpatched/protein-translation/solution.py index e8c2ac29b..b17f4c36b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/protein-translation/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/protein-translation/solution.py @@ -1,25 +1,38 @@ -CODONS = {'AUG': 'Methionine', 'UUU': 'Phenylalanine', - 'UUC': 'Phenylalanine', 'UUA': 'Leucine', 'UUG': 'Leucine', - 'UCU': 'Serine', 'UCC': 'Serine', 'UCA': 'Serine', - 'UCG': 'Serine', 'UAU': 'Tyrosine', 'UAC': 'Tyrosine', - 'UGU': 'Cysteine', 'UGC': 'Cysteine', 'UGG': 'Tryptophan', - 'UAA': 'STOP', 'UAG': 'STOP', 'UGA': 'STOP'} +CODONS = { + "AUG": "Methionine", + "UUU": "Phenylalanine", + "UUC": "Phenylalanine", + "UUA": "Leucine", + "UUG": "Leucine", + "UCU": "Serine", + "UCC": "Serine", + "UCA": "Serine", + "UCG": "Serine", + "UAU": "Tyrosine", + "UAC": "Tyrosine", + "UGU": "Cysteine", + "UGC": "Cysteine", + "UGG": "Tryptophan", + "UAA": "STOP", + "UAG": "STOP", + "UGA": "STOP", +} def of_codon(codon): if codon not in CODONS: - raise ValueError(f'Invalid codon: {codon}') + raise ValueError(f"Invalid codon: {codon}") return CODONS[codon] def proteins(strand): protein_list = [] for codon in map(of_codon, _chunkstring(strand, 3)): - if codon == 'STOP': + if codon == "STOP": break protein_list.append(codon) return protein_list def _chunkstring(string, number): - return (string[idx:number + idx] for idx in range(0, len(string), number)) + return (string[idx : number + idx] for idx in range(0, len(string), number)) diff --git a/evaluator/datasets/polyglot_py_unpatched/proverb/solution.py b/evaluator/datasets/polyglot_py_unpatched/proverb/solution.py index 8e6f30f55..b03281309 100644 --- a/evaluator/datasets/polyglot_py_unpatched/proverb/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/proverb/solution.py @@ -2,10 +2,12 @@ def proverb(*rhyme_items, qualifier): print(rhyme_items) if not rhyme_items: return [] - phrases = [f'For want of a {element_1} the {element_2} was lost.' - for element_1, element_2 in zip(rhyme_items, rhyme_items[1:])] + phrases = [ + f"For want of a {element_1} the {element_2} was lost." + for element_1, element_2 in zip(rhyme_items, rhyme_items[1:]) + ] if qualifier: - phrases.append(f'And all for the want of a {qualifier} {rhyme_items[0]}.') + phrases.append(f"And all for the want of a {qualifier} {rhyme_items[0]}.") else: - phrases.append(f'And all for the want of a {rhyme_items[0]}.') + phrases.append(f"And all for the want of a {rhyme_items[0]}.") return phrases diff --git a/evaluator/datasets/polyglot_py_unpatched/proverb/tests.py b/evaluator/datasets/polyglot_py_unpatched/proverb/tests.py index 7d1ebbb44..8ac7f7659 100644 --- a/evaluator/datasets/polyglot_py_unpatched/proverb/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/proverb/tests.py @@ -20,9 +20,7 @@ def test_zero_pieces(self): def test_one_piece(self): input_data = ["nail"] - self.assertEqual( - proverb(*input_data, qualifier=None), ["And all for the want of a nail."] - ) + self.assertEqual(proverb(*input_data, qualifier=None), ["And all for the want of a nail."]) def test_two_pieces(self): input_data = ["nail", "shoe"] diff --git a/evaluator/datasets/polyglot_py_unpatched/pythagorean-triplet/solution.py b/evaluator/datasets/polyglot_py_unpatched/pythagorean-triplet/solution.py index 40b91444b..50dcaa8bf 100644 --- a/evaluator/datasets/polyglot_py_unpatched/pythagorean-triplet/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/pythagorean-triplet/solution.py @@ -1,4 +1,4 @@ -from math import sqrt, ceil, gcd +from math import ceil, gcd, sqrt def triplets_in_range(start, end): @@ -36,8 +36,8 @@ def primitive_triplets(limit): for more information """ for member_1, member_2 in euclidian_coprimes(limit): - calc_1 = member_1 ** 2 - calc_2 = member_2 ** 2 + calc_1 = member_1**2 + calc_2 = member_2**2 alpha = calc_1 - calc_2 beta = 2 * member_1 * member_2 @@ -50,8 +50,4 @@ def primitive_triplets(limit): def triplets_with_sum(number): - return [ - triplet for triplet - in triplets_in_range(1, number // 2) - if sum(triplet) == number - ] + return [triplet for triplet in triplets_in_range(1, number // 2) if sum(triplet) == number] diff --git a/evaluator/datasets/polyglot_py_unpatched/queen-attack/solution.py b/evaluator/datasets/polyglot_py_unpatched/queen-attack/solution.py index e9ae242ff..0edcb202f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/queen-attack/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/queen-attack/solution.py @@ -1,13 +1,13 @@ class Queen: def __init__(self, row, column): if row < 0: - raise ValueError('row not positive') + raise ValueError("row not positive") if not 0 <= row <= 7: - raise ValueError('row not on board') + raise ValueError("row not on board") if column < 0: - raise ValueError('column not positive') + raise ValueError("column not positive") if not 0 <= column <= 7: - raise ValueError('column not on board') + raise ValueError("column not on board") self.row = row self.column = column @@ -15,7 +15,7 @@ def can_attack(self, another_queen): idx = abs(self.row - another_queen.row) edx = abs(self.column - another_queen.column) if idx == edx == 0: - raise ValueError('Invalid queen position: both queens in the same square') + raise ValueError("Invalid queen position: both queens in the same square") elif idx == edx or idx == 0 or edx == 0: return True else: diff --git a/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/solution.py b/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/solution.py index a27bd2670..307d01853 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/solution.py @@ -1,4 +1,4 @@ -from itertools import cycle, chain +from itertools import chain, cycle def fence_pattern(rails, size): @@ -8,10 +8,10 @@ def fence_pattern(rails, size): def encode(msg, rails): fence = fence_pattern(rails, len(msg)) - return ''.join(msg[idx] for _, idx in sorted(fence)) + return "".join(msg[idx] for _, idx in sorted(fence)) def decode(msg, rails): fence = fence_pattern(rails, len(msg)) fence_msg = zip(msg, sorted(fence)) - return ''.join(char for char, _ in sorted(fence_msg, key=lambda item: item[1][1])) + return "".join(char for char, _ in sorted(fence_msg, key=lambda item: item[1][1])) diff --git a/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/tests.py b/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/tests.py index 139752fd9..09285342c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/rail-fence-cipher/tests.py @@ -15,17 +15,13 @@ def test_encode_with_two_rails(self): self.assertMultiLineEqual(encode("XOXOXOXOXOXOXOXOXO", 2), "XXXXXXXXXOOOOOOOOO") def test_encode_with_three_rails(self): - self.assertMultiLineEqual( - encode("WEAREDISCOVEREDFLEEATONCE", 3), "WECRLTEERDSOEEFEAOCAIVDEN" - ) + self.assertMultiLineEqual(encode("WEAREDISCOVEREDFLEEATONCE", 3), "WECRLTEERDSOEEFEAOCAIVDEN") def test_encode_with_ending_in_the_middle(self): self.assertMultiLineEqual(encode("EXERCISES", 4), "ESXIEECSR") def test_decode_with_three_rails(self): - self.assertMultiLineEqual( - decode("TEITELHDVLSNHDTISEIIEA", 3), "THEDEVILISINTHEDETAILS" - ) + self.assertMultiLineEqual(decode("TEITELHDVLSNHDTISEIIEA", 3), "THEDEVILISINTHEDETAILS") def test_decode_with_five_rails(self): self.assertMultiLineEqual(decode("EIEXMSMESAORIWSCE", 5), "EXERCISMISAWESOME") diff --git a/evaluator/datasets/polyglot_py_unpatched/raindrops/solution.py b/evaluator/datasets/polyglot_py_unpatched/raindrops/solution.py index 97eeadc33..4160ced6e 100644 --- a/evaluator/datasets/polyglot_py_unpatched/raindrops/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/raindrops/solution.py @@ -3,13 +3,13 @@ def convert(number): Converts a number to a string according to the raindrop sounds. """ - result = '' + result = "" if number % 3 == 0: - result += 'Pling' + result += "Pling" if number % 5 == 0: - result += 'Plang' + result += "Plang" if number % 7 == 0: - result += 'Plong' + result += "Plong" if not result: result = str(number) diff --git a/evaluator/datasets/polyglot_py_unpatched/rational-numbers/main.py b/evaluator/datasets/polyglot_py_unpatched/rational-numbers/main.py index d553c30b1..b18cbdf3e 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rational-numbers/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/rational-numbers/main.py @@ -7,7 +7,7 @@ def __eq__(self, other): return self.numer == other.numer and self.denom == other.denom def __repr__(self): - return f'{self.numer}/{self.denom}' + return f"{self.numer}/{self.denom}" def __add__(self, other): pass diff --git a/evaluator/datasets/polyglot_py_unpatched/rational-numbers/solution.py b/evaluator/datasets/polyglot_py_unpatched/rational-numbers/solution.py index d8a0b87be..df44b540f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rational-numbers/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/rational-numbers/solution.py @@ -17,7 +17,7 @@ def __eq__(self, other): return self.numer == other.numer and self.denom == other.denom def __repr__(self): - return f'{self.numer}/{self.denom}' + return f"{self.numer}/{self.denom}" def __add__(self, other): numer = (self.numer * other.denom) + (other.numer * self.denom) diff --git a/evaluator/datasets/polyglot_py_unpatched/rational-numbers/tests.py b/evaluator/datasets/polyglot_py_unpatched/rational-numbers/tests.py index 91c11073e..3e0c94260 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rational-numbers/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/rational-numbers/tests.py @@ -10,7 +10,6 @@ class RationalNumbersTest(unittest.TestCase): - # Tests of type: Arithmetic # Addition diff --git a/evaluator/datasets/polyglot_py_unpatched/react/main.py b/evaluator/datasets/polyglot_py_unpatched/react/main.py index ab6be311d..03ff02e78 100644 --- a/evaluator/datasets/polyglot_py_unpatched/react/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/react/main.py @@ -12,4 +12,3 @@ def add_callback(self, callback): def remove_callback(self, callback): pass - \ No newline at end of file diff --git a/evaluator/datasets/polyglot_py_unpatched/react/tests.py b/evaluator/datasets/polyglot_py_unpatched/react/tests.py index 70cc849e8..24d538f56 100644 --- a/evaluator/datasets/polyglot_py_unpatched/react/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/react/tests.py @@ -2,12 +2,12 @@ # https://github.com/exercism/problem-specifications/tree/main/exercises/react/canonical-data.json # File last updated on 2023-07-19 -from functools import partial import unittest +from functools import partial from main import ( - InputCell, ComputeCell, + InputCell, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/rectangles/solution.py b/evaluator/datasets/polyglot_py_unpatched/rectangles/solution.py index fe665729d..ae0df4fa2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rectangles/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/rectangles/solution.py @@ -8,7 +8,7 @@ def __init__(self, idx, jdx): self.jdx = jdx def __str__(self): - return '[' + str(self.idx) + ', ' + str(self.jdx) + ']' + return "[" + str(self.idx) + ", " + str(self.jdx) + "]" # return corner on the same line @@ -29,9 +29,12 @@ def same_col(index, list_obj): def search_corners(list_obj): - return [Corners(item, element) for item in range(len(list_obj)) - for element in range(len(list_obj[item])) - if list_obj[item][element] == '+'] + return [ + Corners(item, element) + for item in range(len(list_obj)) + for element in range(len(list_obj[item])) + if list_obj[item][element] == "+" + ] # validate that 4 points form a rectangle by @@ -58,16 +61,14 @@ def possible_rect(quartet): # validate path between two corners def path(corner1, corner2, item): if corner1.idx == corner2.idx: - for jdx in range(min(corner1.jdx + 1, corner2.jdx + 1), - max(corner1.jdx, corner2.jdx)): - if item[corner1.idx][jdx] != '-' and item[corner1.idx][jdx] != '+': + for jdx in range(min(corner1.jdx + 1, corner2.jdx + 1), max(corner1.jdx, corner2.jdx)): + if item[corner1.idx][jdx] != "-" and item[corner1.idx][jdx] != "+": return False return True elif corner1.jdx == corner2.jdx: - for idx in range(min(corner1.idx + 1, corner2.idx + 1), - max(corner1.idx, corner2.idx)): - if item[idx][corner1.jdx] != '|' and item[idx][corner1.jdx] != '+': + for idx in range(min(corner1.idx + 1, corner2.idx + 1), max(corner1.idx, corner2.idx)): + if item[idx][corner1.jdx] != "|" and item[idx][corner1.jdx] != "+": return False return True return None @@ -78,8 +79,8 @@ def validate_rect(rectangle, item): # validate connection at every corner # with neighbours on the same line and col for idx, _ in enumerate(rectangle): - line = same_line(rectangle[idx].idx, rectangle[0:idx] + rectangle[idx + 1:]) - column = same_col(rectangle[idx].jdx, rectangle[0:idx] + rectangle[idx + 1:]) + line = same_line(rectangle[idx].idx, rectangle[0:idx] + rectangle[idx + 1 :]) + column = same_col(rectangle[idx].jdx, rectangle[0:idx] + rectangle[idx + 1 :]) if not path(rectangle[idx], line, item) or not path(rectangle[idx], column, item): return False @@ -88,7 +89,7 @@ def validate_rect(rectangle, item): # count number of rectangles inside ASCII in input lines -def rectangles(strings=''): +def rectangles(strings=""): rectangle_total = 0 # test empty str if not strings: diff --git a/evaluator/datasets/polyglot_py_unpatched/resistor-color-duo/solution.py b/evaluator/datasets/polyglot_py_unpatched/resistor-color-duo/solution.py index 0f1285939..f7ad05d98 100644 --- a/evaluator/datasets/polyglot_py_unpatched/resistor-color-duo/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/resistor-color-duo/solution.py @@ -1,15 +1,4 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] def value(colors): diff --git a/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/solution.py b/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/solution.py index 0fd6e42fc..d447314c0 100644 --- a/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/solution.py @@ -1,49 +1,38 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] COLORS_TOLERANCE = { - 'brown': 1, - 'red': 2, - 'green': 0.5, - 'blue': 0.25, - 'violet': 0.1, - 'grey': 0.05, - 'gold': 5, - 'silver': 10 + "brown": 1, + "red": 2, + "green": 0.5, + "blue": 0.25, + "violet": 0.1, + "grey": 0.05, + "gold": 5, + "silver": 10, } def resistor_label(colors): if len(colors) == 1: - return f'0 ohms' + return "0 ohms" elif len(colors) == 4: value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1]) value *= 10 ** COLORS.index(colors[2]) value, unit = color_code(value) value = int(value) if value.is_integer() else value - return f'{value} {unit} ±{COLORS_TOLERANCE[colors[3]]}%' + return f"{value} {unit} ±{COLORS_TOLERANCE[colors[3]]}%" else: value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2]) value *= 10 ** COLORS.index(colors[3]) value, unit = color_code(value) value = int(value) if value.is_integer() else value - return f'{value} {unit} ±{COLORS_TOLERANCE[colors[4]]}%' + return f"{value} {unit} ±{COLORS_TOLERANCE[colors[4]]}%" def color_code(color): if color < 1000: - return color / 1, 'ohms' + return color / 1, "ohms" elif color < 1000000: - return color / 1000, 'kiloohms' + return color / 1000, "kiloohms" else: - return color / 1000000, 'megaohms' \ No newline at end of file + return color / 1000000, "megaohms" diff --git a/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/tests.py b/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/tests.py index a46a70a76..9f97d0692 100644 --- a/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/resistor-color-expert/tests.py @@ -18,44 +18,28 @@ def test_red_black_red_and_green(self): self.assertEqual(resistor_label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%") def test_green_brown_orange_and_grey(self): - self.assertEqual( - resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%" - ) + self.assertEqual(resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%") def test_one_black_band(self): self.assertEqual(resistor_label(["black"]), "0 ohms") def test_orange_orange_yellow_black_and_brown(self): - self.assertEqual( - resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%" - ) + self.assertEqual(resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%") def test_red_green_yellow_yellow_and_brown(self): - self.assertEqual( - resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%" - ) + self.assertEqual(resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%") def test_blue_grey_white_brown_and_brown(self): - self.assertEqual( - resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%" - ) + self.assertEqual(resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%") def test_violet_orange_red_and_grey(self): - self.assertEqual( - resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%" - ) + self.assertEqual(resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%") def test_brown_red_orange_green_and_blue(self): - self.assertEqual( - resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%" - ) + self.assertEqual(resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%") def test_brown_black_brown_yellow_and_violet(self): - self.assertEqual( - resistor_label(["brown", "black", "brown", "yellow", "violet"]), "1.01 megaohms ±0.1%" - ) + self.assertEqual(resistor_label(["brown", "black", "brown", "yellow", "violet"]), "1.01 megaohms ±0.1%") def test_brown_black_red_and_red(self): - self.assertEqual( - resistor_label(["brown", "black", "red", "red"]), "1 kiloohms ±2%" - ) + self.assertEqual(resistor_label(["brown", "black", "red", "red"]), "1 kiloohms ±2%") diff --git a/evaluator/datasets/polyglot_py_unpatched/resistor-color-trio/solution.py b/evaluator/datasets/polyglot_py_unpatched/resistor-color-trio/solution.py index 69554592d..d07a70160 100644 --- a/evaluator/datasets/polyglot_py_unpatched/resistor-color-trio/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/resistor-color-trio/solution.py @@ -1,15 +1,4 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] def label(colors): @@ -17,16 +6,16 @@ def label(colors): value *= 10 ** COLORS.index(colors[2]) label = str(value) - if len(label) < 4 : - unit = 'ohms' + if len(label) < 4: + unit = "ohms" elif len(label) < 7: - label = str(value//1000) - unit = 'kiloohms' - elif len(label) <= 8 : - label = str(value//1000000) - unit = 'megaohms' + label = str(value // 1000) + unit = "kiloohms" + elif len(label) <= 8: + label = str(value // 1000000) + unit = "megaohms" elif len(label) >= 9: - label = str(value//1000000000) - unit = 'gigaohms' + label = str(value // 1000000000) + unit = "gigaohms" - return f'{value if value < 1000 else label} {unit}' + return f"{value if value < 1000 else label} {unit}" diff --git a/evaluator/datasets/polyglot_py_unpatched/resistor-color/solution.py b/evaluator/datasets/polyglot_py_unpatched/resistor-color/solution.py index 83c67f4f1..12b918ccb 100644 --- a/evaluator/datasets/polyglot_py_unpatched/resistor-color/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/resistor-color/solution.py @@ -1,15 +1,4 @@ -COLORS = [ - 'black', - 'brown', - 'red', - 'orange', - 'yellow', - 'green', - 'blue', - 'violet', - 'grey', - 'white' -] +COLORS = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "grey", "white"] def color_code(color): diff --git a/evaluator/datasets/polyglot_py_unpatched/rest-api/solution.py b/evaluator/datasets/polyglot_py_unpatched/rest-api/solution.py index 46556efce..7979fde61 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rest-api/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/rest-api/solution.py @@ -3,12 +3,12 @@ class RestAPI: def __init__(self, database=None): - self.database = database or {'users': []} + self.database = database or {"users": []} def update(self): - for user in self.database['users']: - owed_by = user['owed_by'] - owes = user['owes'] + for user in self.database["users"]: + owed_by = user["owed_by"] + owes = user["owes"] for debtor in list(owed_by.keys()): if debtor in owes: diff = 0 @@ -22,65 +22,54 @@ def update(self): owes[debtor] = diff elif diff < 0: owed_by[debtor] = -diff - user['balance'] = sum(owed_by.values()) - sum(owes.values()) + user["balance"] = sum(owed_by.values()) - sum(owes.values()) def get(self, url, payload=None): if payload is not None: payload = json.loads(payload) - if url == '/users': + if url == "/users": if payload is None: return json.dumps(self.database) else: - return json.dumps({ - 'users': [ - user for user in self.database['users'] - if user['name'] in payload['users'] - ] - }) + return json.dumps( + {"users": [user for user in self.database["users"] if user["name"] in payload["users"]]} + ) return None def post(self, url, payload=None): result = None if payload is not None: payload = json.loads(payload) - if url == '/add': + if url == "/add": if payload is not None: - name = payload['user'] - users = self.database['users'] + name = payload["user"] + users = self.database["users"] user = None for idx in users: - if idx['name'] == name: + if idx["name"] == name: user = idx break if user is None: - new_user = { - 'name': name, - 'owes': {}, - 'owed_by': {}, - 'balance': 0 - } + new_user = {"name": name, "owes": {}, "owed_by": {}, "balance": 0} users.append(new_user) self.update() result = json.dumps(new_user) - elif url == '/iou': + elif url == "/iou": if payload is not None: - lender_name = payload['lender'] - borrower_name = payload['borrower'] - amount = payload['amount'] + lender_name = payload["lender"] + borrower_name = payload["borrower"] + amount = payload["amount"] lender = borrower = None - for user in self.database['users']: - if user['name'] == lender_name: + for user in self.database["users"]: + if user["name"] == lender_name: lender = user - elif user['name'] == borrower_name: + elif user["name"] == borrower_name: borrower = user if lender is not None and borrower is not None: - lender['owed_by'].setdefault(borrower_name, 0) - lender['owed_by'][borrower_name] += amount - borrower['owes'].setdefault(lender_name, 0) - borrower['owes'][lender_name] += amount + lender["owed_by"].setdefault(borrower_name, 0) + lender["owed_by"][borrower_name] += amount + borrower["owes"].setdefault(lender_name, 0) + borrower["owes"][lender_name] += amount self.update() - result = self.get( - '/users', - json.dumps({'users': [lender_name, borrower_name]}) - ) + result = self.get("/users", json.dumps({"users": [lender_name, borrower_name]})) return result diff --git a/evaluator/datasets/polyglot_py_unpatched/rest-api/tests.py b/evaluator/datasets/polyglot_py_unpatched/rest-api/tests.py index aa334bca0..3b5f95b89 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rest-api/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/rest-api/tests.py @@ -37,9 +37,7 @@ def test_get_single_user(self): api = RestAPI(database) payload = json.dumps({"users": ["Bob"]}) response = api.get("/users", payload) - expected = { - "users": [{"name": "Bob", "owes": {}, "owed_by": {}, "balance": 0.0}] - } + expected = {"users": [{"name": "Bob", "owes": {}, "owed_by": {}, "balance": 0.0}]} self.assertDictEqual(json.loads(response), expected) def test_both_users_have_0_balance(self): diff --git a/evaluator/datasets/polyglot_py_unpatched/reverse-string/solution.py b/evaluator/datasets/polyglot_py_unpatched/reverse-string/solution.py index c8b38d579..3d6b8fb8c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/reverse-string/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/reverse-string/solution.py @@ -1,2 +1,2 @@ -def reverse(text=''): +def reverse(text=""): return text[::-1] diff --git a/evaluator/datasets/polyglot_py_unpatched/rna-transcription/solution.py b/evaluator/datasets/polyglot_py_unpatched/rna-transcription/solution.py index 61db942ea..ed94e045c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rna-transcription/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/rna-transcription/solution.py @@ -1,4 +1,5 @@ DNA_TO_RNA = str.maketrans("AGCT", "UCGA") + def to_rna(dna_strand): return dna_strand.translate(DNA_TO_RNA) diff --git a/evaluator/datasets/polyglot_py_unpatched/robot-name/solution.py b/evaluator/datasets/polyglot_py_unpatched/robot-name/solution.py index 405142718..b76a77be5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/robot-name/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/robot-name/solution.py @@ -1,26 +1,21 @@ import random -ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + class Robot: def __init__(self): self._name = None self._past_names = set() def prefix(self): - return ''.join([ - random.choice(ALPHABET) - for _ in range(0, 2) - ]) + return "".join([random.choice(ALPHABET) for _ in range(0, 2)]) def suffix(self): - return ''.join([ - str(random.choice(range(0, 10))) - for _ in range(0, 3) - ]) + return "".join([str(random.choice(range(0, 10))) for _ in range(0, 3)]) def get_name(self): if not self._name: - # Collision detection while True: self._name = self.prefix() + self.suffix() diff --git a/evaluator/datasets/polyglot_py_unpatched/robot-name/tests.py b/evaluator/datasets/polyglot_py_unpatched/robot-name/tests.py index 2cc8e2bf1..5fe5ece27 100644 --- a/evaluator/datasets/polyglot_py_unpatched/robot-name/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/robot-name/tests.py @@ -1,5 +1,5 @@ -import unittest import random +import unittest from main import Robot @@ -10,7 +10,7 @@ class RobotNameTest(unittest.TestCase): if not hasattr(unittest.TestCase, "assertRegex"): assertRegex = unittest.TestCase.assertRegexpMatches - name_re = r'^[A-Z]{2}\d{3}$' + name_re = r"^[A-Z]{2}\d{3}$" def test_has_name(self): self.assertRegex(Robot().name, self.name_re) @@ -21,10 +21,7 @@ def test_name_sticks(self): self.assertEqual(robot.name, robot.name) def test_different_robots_have_different_names(self): - self.assertNotEqual( - Robot().name, - Robot().name - ) + self.assertNotEqual(Robot().name, Robot().name) def test_reset_name(self): # Set a seed @@ -47,5 +44,5 @@ def test_reset_name(self): self.assertRegex(name2, self.name_re) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/robot-simulator/solution.py b/evaluator/datasets/polyglot_py_unpatched/robot-simulator/solution.py index 20caf3bed..f34748f07 100644 --- a/evaluator/datasets/polyglot_py_unpatched/robot-simulator/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/robot-simulator/solution.py @@ -37,9 +37,7 @@ def turn_right(self): self.compass.right() def move(self, commands): - instructions = {'A': self.advance, - 'R': self.turn_right, - 'L': self.turn_left} + instructions = {"A": self.advance, "R": self.turn_right, "L": self.turn_left} for cmd in commands: if cmd in instructions: instructions[cmd]() diff --git a/evaluator/datasets/polyglot_py_unpatched/robot-simulator/tests.py b/evaluator/datasets/polyglot_py_unpatched/robot-simulator/tests.py index b09f45a3f..0c283f68f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/robot-simulator/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/robot-simulator/tests.py @@ -5,16 +5,15 @@ import unittest from main import ( - Robot, - NORTH, EAST, + NORTH, SOUTH, WEST, + Robot, ) class RobotSimulatorTest(unittest.TestCase): - # Test create robot def test_at_origin_facing_north(self): robot = Robot(NORTH, 0, 0) diff --git a/evaluator/datasets/polyglot_py_unpatched/roman-numerals/main.py b/evaluator/datasets/polyglot_py_unpatched/roman-numerals/main.py index 96f266dc4..f52e854e1 100644 --- a/evaluator/datasets/polyglot_py_unpatched/roman-numerals/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/roman-numerals/main.py @@ -1,3 +1,2 @@ def roman(number): pass - diff --git a/evaluator/datasets/polyglot_py_unpatched/roman-numerals/solution.py b/evaluator/datasets/polyglot_py_unpatched/roman-numerals/solution.py index c6e56ec03..a9fbd19e6 100644 --- a/evaluator/datasets/polyglot_py_unpatched/roman-numerals/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/roman-numerals/solution.py @@ -1,16 +1,22 @@ NUMERAL_MAPPINGS = ( - (1000, 'M'), (900, 'CM'), - (500, 'D'), (400, 'CD'), - (100, 'C'), (90, 'XC'), - (50, 'L'), (40, 'XL'), - (10, 'X'), (9, 'IX'), - (5, 'V'), (4, 'IV'), - (1, 'I') + (1000, "M"), + (900, "CM"), + (500, "D"), + (400, "CD"), + (100, "C"), + (90, "XC"), + (50, "L"), + (40, "XL"), + (10, "X"), + (9, "IX"), + (5, "V"), + (4, "IV"), + (1, "I"), ) def roman(number): - result = '' + result = "" for arabic_num, roman_num in NUMERAL_MAPPINGS: while number >= arabic_num: result += roman_num diff --git a/evaluator/datasets/polyglot_py_unpatched/rotational-cipher/solution.py b/evaluator/datasets/polyglot_py_unpatched/rotational-cipher/solution.py index 332632754..6fb35dcf3 100644 --- a/evaluator/datasets/polyglot_py_unpatched/rotational-cipher/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/rotational-cipher/solution.py @@ -1,11 +1,10 @@ from string import ascii_lowercase, ascii_uppercase - ALPHA_LEN = len(ascii_lowercase) def rotate(message, key): - coded_message = '' + coded_message = "" for char in message: if char in ascii_lowercase: char = ascii_lowercase[(ascii_lowercase.index(char) + key) % ALPHA_LEN] diff --git a/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/solution.py b/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/solution.py index 309c8a3b1..33e60209c 100644 --- a/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/solution.py @@ -3,11 +3,12 @@ def decode(string): - return sub(r'(\d+)(\D)', lambda main: main.group(2) * int(main.group(1)), string) + return sub(r"(\d+)(\D)", lambda main: main.group(2) * int(main.group(1)), string) def encode(string): def single_helper(key, group): size = len(list(group)) return key if size == 1 else str(size) + key - return ''.join(single_helper(key, group) for key, group in groupby(string)) + + return "".join(single_helper(key, group) for key, group in groupby(string)) diff --git a/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/tests.py b/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/tests.py index 042924eee..0217da5d5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/run-length-encoding/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - encode, decode, + encode, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/saddle-points/solution.py b/evaluator/datasets/polyglot_py_unpatched/saddle-points/solution.py index ff11e68a5..86a7cd370 100644 --- a/evaluator/datasets/polyglot_py_unpatched/saddle-points/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/saddle-points/solution.py @@ -3,14 +3,16 @@ def saddle_points(matrix): return [] if any(len(row) != len(matrix[0]) for row in matrix): - raise ValueError('irregular matrix') + raise ValueError("irregular matrix") mmax = [max(row) for row in matrix] mmin = [min(col) for col in zip(*matrix)] - points = [{'row': index + 1, 'column': col_index + 1} - for index, _ in enumerate(matrix) - for col_index, _ in enumerate(matrix[0]) - if mmax[index] == mmin[col_index]] + points = [ + {"row": index + 1, "column": col_index + 1} + for index, _ in enumerate(matrix) + for col_index, _ in enumerate(matrix[0]) + if mmax[index] == mmin[col_index] + ] return points or [] diff --git a/evaluator/datasets/polyglot_py_unpatched/satellite/solution.py b/evaluator/datasets/polyglot_py_unpatched/satellite/solution.py index 310a89aee..fef73ef9a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/satellite/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/satellite/solution.py @@ -1,16 +1,16 @@ def tree_from_traversals(preorder, inorder): if len(preorder) != len(inorder): - raise ValueError('traversals must have the same length') + raise ValueError("traversals must have the same length") if set(preorder) != set(inorder): - raise ValueError('traversals must have the same elements') + raise ValueError("traversals must have the same elements") if len(set(preorder)) != len(preorder) != len(set(inorder)): - raise ValueError('traversals must contain unique items') + raise ValueError("traversals must contain unique items") if not preorder: return {} value = preorder.pop(0) index = inorder.index(value) - left_inorder, right_inorder = inorder[:index], inorder[index+1:] + left_inorder, right_inorder = inorder[:index], inorder[index + 1 :] left_preorder = [idx for idx in preorder if idx in left_inorder] right_preorder = [idx for idx in preorder if idx in right_inorder] @@ -18,4 +18,4 @@ def tree_from_traversals(preorder, inorder): left = tree_from_traversals(left_preorder, left_inorder) right = tree_from_traversals(right_preorder, right_inorder) - return {'v': value, 'l': left, 'r': right} + return {"v": value, "l": left, "r": right} diff --git a/evaluator/datasets/polyglot_py_unpatched/satellite/tests.py b/evaluator/datasets/polyglot_py_unpatched/satellite/tests.py index 9bc8118c0..588fb4fdc 100644 --- a/evaluator/datasets/polyglot_py_unpatched/satellite/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/satellite/tests.py @@ -55,9 +55,7 @@ def test_reject_inconsistent_traversals_of_same_length(self): with self.assertRaises(ValueError) as err: tree_from_traversals(preorder, inorder) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "traversals must have the same elements" - ) + self.assertEqual(err.exception.args[0], "traversals must have the same elements") def test_reject_traversals_with_repeated_items(self): preorder = ["a", "b", "a"] diff --git a/evaluator/datasets/polyglot_py_unpatched/say/solution.py b/evaluator/datasets/polyglot_py_unpatched/say/solution.py index 95d6d4477..7a409f4e1 100644 --- a/evaluator/datasets/polyglot_py_unpatched/say/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/say/solution.py @@ -1,11 +1,41 @@ def say(number): - small = dict(enumerate(( - 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', - 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', - 'sixteen', 'seventeen', 'eighteen', 'nineteen'))) + small = dict( + enumerate( + ( + "zero", + "one", + "two", + "three", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "ten", + "eleven", + "twelve", + "thirteen", + "fourteen", + "fifteen", + "sixteen", + "seventeen", + "eighteen", + "nineteen", + ) + ) + ) - tens = {20: 'twenty', 30: 'thirty', 40: 'forty', 50: 'fifty', - 60: 'sixty', 70: 'seventy', 80: 'eighty', 90: 'ninety'} + tens = { + 20: "twenty", + 30: "thirty", + 40: "forty", + 50: "fifty", + 60: "sixty", + 70: "seventy", + 80: "eighty", + 90: "ninety", + } kilo = 1e3 mega = 1e6 @@ -13,9 +43,9 @@ def say(number): tera = 1e12 if number < 0: - raise ValueError('input out of range') + raise ValueError("input out of range") if number >= tera: - raise ValueError('input out of range') + raise ValueError("input out of range") if number < 20: return small[number] @@ -23,23 +53,23 @@ def say(number): if number < 100: if number % 10 == 0: return tens[number] - return tens[number // 10 * 10] + '-' + small[number % 10] + return tens[number // 10 * 10] + "-" + small[number % 10] if number < kilo: if number % 100 == 0: - return small[number // 100] + ' hundred' - return small[number // 100] + ' hundred ' + say(number % 100) + return small[number // 100] + " hundred" + return small[number // 100] + " hundred " + say(number % 100) if number < mega: if number % kilo == 0: - return say(number // kilo) + ' thousand' - return say(number // kilo) + ' thousand ' + say(number % kilo) + return say(number // kilo) + " thousand" + return say(number // kilo) + " thousand " + say(number % kilo) if number < giga: if number % mega == 0: - return say(number // mega) + ' million' - return say(number // mega) + ' million ' + say(number % mega) + return say(number // mega) + " million" + return say(number // mega) + " million " + say(number % mega) if number % giga == 0: - return say(number // giga) + ' billion' - return say(number // giga) + ' billion ' + say(number % giga) \ No newline at end of file + return say(number // giga) + " billion" + return say(number // giga) + " billion " + say(number % giga) diff --git a/evaluator/datasets/polyglot_py_unpatched/say/tests.py b/evaluator/datasets/polyglot_py_unpatched/say/tests.py index 3548a0d79..3d27a96a7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/say/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/say/tests.py @@ -53,9 +53,7 @@ def test_one_million(self): self.assertEqual(say(1000000), "one million") def test_one_million_two_thousand_three_hundred_forty_five(self): - self.assertEqual( - say(1002345), "one million two thousand three hundred forty-five" - ) + self.assertEqual(say(1002345), "one million two thousand three hundred forty-five") def test_one_billion(self): self.assertEqual(say(1000000000), "one billion") diff --git a/evaluator/datasets/polyglot_py_unpatched/scale-generator/solution.py b/evaluator/datasets/polyglot_py_unpatched/scale-generator/solution.py index 19c0c816c..22dd4a9e5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/scale-generator/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/scale-generator/solution.py @@ -1,13 +1,13 @@ class Scale: - ASCENDING_INTERVALS = ['m', 'M', 'A'] - CHROMATIC_SCALE = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'] - FLAT_CHROMATIC_SCALE = ['A', 'Bb', 'B', 'C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab'] - FLAT_KEYS = ['F', 'Bb', 'Eb', 'Ab', 'Db', 'Gb', 'd', 'g', 'c', 'f', 'bb', 'eb'] + ASCENDING_INTERVALS = ["m", "M", "A"] + CHROMATIC_SCALE = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"] + FLAT_CHROMATIC_SCALE = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"] + FLAT_KEYS = ["F", "Bb", "Eb", "Ab", "Db", "Gb", "d", "g", "c", "f", "bb", "eb"] def __init__(self, tonic, intervals=None): self.tonic = tonic.capitalize() self.intervals = intervals - self.chromatic_scale = (self.FLAT_CHROMATIC_SCALE if tonic in self.FLAT_KEYS else self.CHROMATIC_SCALE) + self.chromatic_scale = self.FLAT_CHROMATIC_SCALE if tonic in self.FLAT_KEYS else self.CHROMATIC_SCALE def chromatic(self): return self._reorder_chromatic_scale() @@ -27,4 +27,4 @@ def interval(self, intervals): def _reorder_chromatic_scale(self): index = self.chromatic_scale.index(self.tonic) - return self.chromatic_scale[index:] + self.chromatic_scale[:index] + return self.chromatic_scale[index:] + self.chromatic_scale[:index] diff --git a/evaluator/datasets/polyglot_py_unpatched/scale-generator/tests.py b/evaluator/datasets/polyglot_py_unpatched/scale-generator/tests.py index d12282aff..3e4028f7b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/scale-generator/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/scale-generator/tests.py @@ -10,7 +10,6 @@ class ScaleGeneratorTest(unittest.TestCase): - # Test chromatic scales def test_chromatic_scale_with_sharps(self): expected = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] diff --git a/evaluator/datasets/polyglot_py_unpatched/scrabble-score/solution.py b/evaluator/datasets/polyglot_py_unpatched/scrabble-score/solution.py index 8ab7d58df..e78f18984 100644 --- a/evaluator/datasets/polyglot_py_unpatched/scrabble-score/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/scrabble-score/solution.py @@ -1,10 +1,30 @@ POINTS = { - 'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, - 'f': 4, 'g': 2, 'h': 4, 'i': 1, 'j': 8, - 'k': 5, 'l': 1, 'm': 3, 'n': 1, 'o': 1, - 'p': 3, 'q': 10, 'r': 1, 's': 1, 't': 1, - 'u': 1, 'v': 4, 'w': 4, 'x': 8, 'y': 4, - 'z': 10 + "a": 1, + "b": 3, + "c": 3, + "d": 2, + "e": 1, + "f": 4, + "g": 2, + "h": 4, + "i": 1, + "j": 8, + "k": 5, + "l": 1, + "m": 3, + "n": 1, + "o": 1, + "p": 3, + "q": 10, + "r": 1, + "s": 1, + "t": 1, + "u": 1, + "v": 4, + "w": 4, + "x": 8, + "y": 4, + "z": 10, } diff --git a/evaluator/datasets/polyglot_py_unpatched/secret-handshake/solution.py b/evaluator/datasets/polyglot_py_unpatched/secret-handshake/solution.py index 14e681d93..7728b5cfa 100644 --- a/evaluator/datasets/polyglot_py_unpatched/secret-handshake/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/secret-handshake/solution.py @@ -1,7 +1,7 @@ -GESTURES = ['jump', 'close your eyes', 'double blink', 'wink'] +GESTURES = ["jump", "close your eyes", "double blink", "wink"] def commands(binary_str): - reverse, *bits = [digit == '1' for digit in binary_str] + reverse, *bits = [digit == "1" for digit in binary_str] actions = [gesture for gesture, bit in zip(GESTURES, bits) if bit] return actions if reverse else actions[::-1] diff --git a/evaluator/datasets/polyglot_py_unpatched/secret-handshake/tests.py b/evaluator/datasets/polyglot_py_unpatched/secret-handshake/tests.py index 45af2106a..9e2078832 100644 --- a/evaluator/datasets/polyglot_py_unpatched/secret-handshake/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/secret-handshake/tests.py @@ -35,14 +35,10 @@ def test_reversing_no_actions_still_gives_no_actions(self): self.assertEqual(commands("10000"), []) def test_all_possible_actions(self): - self.assertEqual( - commands("01111"), ["wink", "double blink", "close your eyes", "jump"] - ) + self.assertEqual(commands("01111"), ["wink", "double blink", "close your eyes", "jump"]) def test_reverse_all_possible_actions(self): - self.assertEqual( - commands("11111"), ["jump", "close your eyes", "double blink", "wink"] - ) + self.assertEqual(commands("11111"), ["jump", "close your eyes", "double blink", "wink"]) def test_do_nothing_for_zero(self): self.assertEqual(commands("00000"), []) diff --git a/evaluator/datasets/polyglot_py_unpatched/series/solution.py b/evaluator/datasets/polyglot_py_unpatched/series/solution.py index 447819fc5..04db528b0 100644 --- a/evaluator/datasets/polyglot_py_unpatched/series/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/series/solution.py @@ -1,11 +1,11 @@ def slices(series, length): if not series: - raise ValueError('series cannot be empty') + raise ValueError("series cannot be empty") elif length == 0: - raise ValueError('slice length cannot be zero') + raise ValueError("slice length cannot be zero") elif length < 0: - raise ValueError('slice length cannot be negative') + raise ValueError("slice length cannot be negative") elif len(series) < length: - raise ValueError('slice length cannot be greater than series length') + raise ValueError("slice length cannot be greater than series length") - return [series[idx:idx + length] for idx in range(len(series) - length + 1)] + return [series[idx : idx + length] for idx in range(len(series) - length + 1)] diff --git a/evaluator/datasets/polyglot_py_unpatched/series/tests.py b/evaluator/datasets/polyglot_py_unpatched/series/tests.py index 47f8b8e9c..e5d0ae082 100644 --- a/evaluator/datasets/polyglot_py_unpatched/series/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/series/tests.py @@ -35,17 +35,13 @@ def test_slice_length_is_too_large(self): with self.assertRaises(ValueError) as err: slices("12345", 6) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "slice length cannot be greater than series length" - ) + self.assertEqual(err.exception.args[0], "slice length cannot be greater than series length") def test_slice_length_is_way_too_large(self): with self.assertRaises(ValueError) as err: slices("12345", 42) self.assertEqual(type(err.exception), ValueError) - self.assertEqual( - err.exception.args[0], "slice length cannot be greater than series length" - ) + self.assertEqual(err.exception.args[0], "slice length cannot be greater than series length") def test_slice_length_cannot_be_zero(self): with self.assertRaises(ValueError) as err: diff --git a/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/solution.py b/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/solution.py index dcdc74c73..82341b9fc 100644 --- a/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/solution.py @@ -1,4 +1,5 @@ """Parse an SGF tree.""" + import collections import string @@ -32,7 +33,7 @@ def parse_property_vals(sgf: str, idx: int) -> tuple[int, list[str]]: while sgf[idx] != "]": # \ has special SGF handling. if sgf[idx] == "\\": - if sgf[idx:idx + 2] == "\\\n": + if sgf[idx : idx + 2] == "\\\n": # Newlines are removed if they come immediately after a \, # otherwise they remain as newlines. pass @@ -75,7 +76,7 @@ def parse_node(sgf: str) -> SgfTree: raise ValueError("propery key is empty") prop_key = sgf[prop_key_start:idx] if not prop_key.isupper(): - raise ValueError('property must be in uppercase') + raise ValueError("property must be in uppercase") idx, prop_vals = parse_property_vals(sgf, idx) properties[prop_key].extend(prop_vals) @@ -106,15 +107,15 @@ def parse_node(sgf: str) -> SgfTree: idx += 1 if idx > prop_key_start and not properties: - raise ValueError('properties without delimiter') + raise ValueError("properties without delimiter") return SgfTree(children=children, properties=dict(properties)) def parse(sgf: str) -> SgfTree: """Parse an SGF tree.""" if not sgf.startswith("(") and not sgf.endswith(")"): - raise ValueError('tree missing') + raise ValueError("tree missing") if not sgf.startswith("(;"): - raise ValueError('tree with no nodes') + raise ValueError("tree with no nodes") inside = sgf[1:-1] return parse_node(inside) diff --git a/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/tests.py b/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/tests.py index f86cf3e5e..0a36b64b5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/sgf-parsing/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - parse, SgfTree, + parse, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/sieve/solution.py b/evaluator/datasets/polyglot_py_unpatched/sieve/solution.py index 0e8ae8ad3..e43d2c299 100644 --- a/evaluator/datasets/polyglot_py_unpatched/sieve/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/sieve/solution.py @@ -1,7 +1,7 @@ def primes(limit): prime = [True] * (limit + 1) prime[0] = prime[1] = False - for idx in range(2, int(limit ** 0.5) + 1): + for idx in range(2, int(limit**0.5) + 1): if prime[idx]: for edx in range(idx * idx, limit + 1, idx): prime[edx] = False diff --git a/evaluator/datasets/polyglot_py_unpatched/simple-cipher/solution.py b/evaluator/datasets/polyglot_py_unpatched/simple-cipher/solution.py index 61abf58d2..9f1d76fee 100644 --- a/evaluator/datasets/polyglot_py_unpatched/simple-cipher/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/simple-cipher/solution.py @@ -1,25 +1,23 @@ -from string import ascii_lowercase -from time import time import random from itertools import cycle +from string import ascii_lowercase +from time import time class Cipher: - def __init__(self, key=None): if key is None: random.seed(time()) - key = ''.join(random.choice(ascii_lowercase) for _ in range(100)) + key = "".join(random.choice(ascii_lowercase) for _ in range(100)) self.key = key def encode(self, text): - return ''.join( - chr(((ord(character) - 2 * ord('a') + ord(key)) % 26) + ord('a')) + return "".join( + chr(((ord(character) - 2 * ord("a") + ord(key)) % 26) + ord("a")) for character, key in zip(text, cycle(self.key)) ) def decode(self, text): - return ''.join( - chr(((ord(character) - ord(key) + 26) % 26) + ord('a')) - for character, key in zip(text, cycle(self.key)) + return "".join( + chr(((ord(character) - ord(key) + 26) % 26) + ord("a")) for character, key in zip(text, cycle(self.key)) ) diff --git a/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/solution.py b/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/solution.py index f87b155e7..41d3ccbf2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/solution.py @@ -44,7 +44,7 @@ def __len__(self): def head(self): if self._head is None: - raise EmptyListException('The list is empty.') + raise EmptyListException("The list is empty.") return self._head def push(self, value): @@ -55,7 +55,7 @@ def push(self, value): def pop(self): if self._head is None: - raise EmptyListException('The list is empty.') + raise EmptyListException("The list is empty.") self._len -= 1 ret = self._head.value() self._head = self._head.next() diff --git a/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/tests.py b/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/tests.py index d999e2b26..7b7451a7f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/simple-linked-list/tests.py @@ -1,10 +1,10 @@ import unittest -from main import LinkedList, EmptyListException - +from main import EmptyListException, LinkedList # No canonical data available for this exercise + class SimpleLinkedListTest(unittest.TestCase): def test_empty_list_has_len_zero(self): sut = LinkedList() diff --git a/evaluator/datasets/polyglot_py_unpatched/space-age/solution.py b/evaluator/datasets/polyglot_py_unpatched/space-age/solution.py index 92c502f54..5a350cf2f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/space-age/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/space-age/solution.py @@ -6,7 +6,6 @@ def inner(self): class SpaceAge: - on_mercury = period_converter(7600530.24) on_venus = period_converter(19413907.2) on_earth = period_converter(31558149.76) diff --git a/evaluator/datasets/polyglot_py_unpatched/spiral-matrix/solution.py b/evaluator/datasets/polyglot_py_unpatched/spiral-matrix/solution.py index cc25fb3c2..0dc69ab89 100644 --- a/evaluator/datasets/polyglot_py_unpatched/spiral-matrix/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/spiral-matrix/solution.py @@ -1,5 +1,5 @@ def spiral_matrix(size): - matrix = [[0]*size for row in range(size)] + matrix = [[0] * size for row in range(size)] idx = 0 jdx = -1 element = 1 @@ -7,8 +7,8 @@ def spiral_matrix(size): digital = [0, 1, 0, -1] disco = [1, 0, -1, 0] - for edx in range(2*size - 1): - for _ in range((2*size - edx) // 2): + for edx in range(2 * size - 1): + for _ in range((2 * size - edx) // 2): idx += digital[edx % 4] jdx += disco[edx % 4] matrix[idx][jdx] = element diff --git a/evaluator/datasets/polyglot_py_unpatched/square-root/solution.py b/evaluator/datasets/polyglot_py_unpatched/square-root/solution.py index 94cd0fdc7..56c7ff401 100644 --- a/evaluator/datasets/polyglot_py_unpatched/square-root/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/square-root/solution.py @@ -1,5 +1,5 @@ def square_root(number): n = 0 - while n ** 2 != number: + while n**2 != number: n += 1 return n diff --git a/evaluator/datasets/polyglot_py_unpatched/strain/solution.py b/evaluator/datasets/polyglot_py_unpatched/strain/solution.py index 6c4b3f815..c4960684b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/strain/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/strain/solution.py @@ -1,5 +1,6 @@ def keep(sequence, predicate): return [element for element in sequence if predicate(element)] + def discard(sequence, predicate): return [element for element in sequence if not predicate(element)] diff --git a/evaluator/datasets/polyglot_py_unpatched/strain/tests.py b/evaluator/datasets/polyglot_py_unpatched/strain/tests.py index 78c6337f2..7995f6eeb 100644 --- a/evaluator/datasets/polyglot_py_unpatched/strain/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/strain/tests.py @@ -1,6 +1,6 @@ import unittest -from main import keep, discard +from main import discard, keep class StrainTest(unittest.TestCase): @@ -22,25 +22,24 @@ def test_keep_everything(self): self.assertEqual(keep(inp, lambda x: x % 2 == 0), inp) def test_discard_endswith(self): - inp = ['dough', 'cash', 'plough', 'though', 'through', 'enough'] - out = ['cash'] - self.assertEqual(discard(inp, lambda x: str.endswith(x, 'ough')), out) + inp = ["dough", "cash", "plough", "though", "through", "enough"] + out = ["cash"] + self.assertEqual(discard(inp, lambda x: str.endswith(x, "ough")), out) def test_keep_z(self): - inp = ['zebra', 'arizona', 'apple', 'google', 'mozilla'] - out = ['zebra', 'arizona', 'mozilla'] - self.assertEqual(keep(inp, lambda x: 'z' in x), out) + inp = ["zebra", "arizona", "apple", "google", "mozilla"] + out = ["zebra", "arizona", "mozilla"] + self.assertEqual(keep(inp, lambda x: "z" in x), out) def test_keep_discard(self): - inp = ['1,2,3', 'one', 'almost!', 'love'] + inp = ["1,2,3", "one", "almost!", "love"] self.assertEqual(discard(keep(inp, str.isalpha), str.isalpha), []) def test_keep_plus_discard(self): - inp = ['1,2,3', 'one', 'almost!', 'love'] - out = ['one', 'love', '1,2,3', 'almost!'] - self.assertEqual( - keep(inp, str.isalpha) + discard(inp, str.isalpha), out) + inp = ["1,2,3", "one", "almost!", "love"] + out = ["one", "love", "1,2,3", "almost!"] + self.assertEqual(keep(inp, str.isalpha) + discard(inp, str.isalpha), out) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/sublist/tests.py b/evaluator/datasets/polyglot_py_unpatched/sublist/tests.py index 15fc9b379..4a3cdfe5b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/sublist/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/sublist/tests.py @@ -5,11 +5,11 @@ import unittest from main import ( - sublist, + EQUAL, SUBLIST, SUPERLIST, - EQUAL, UNEQUAL, + sublist, ) @@ -85,6 +85,4 @@ def test_large_lists(self): ) def test_spread_sublist(self): - self.assertEqual( - sublist(list(range(3, 200, 3)), list(range(15, 200, 15))), UNEQUAL - ) + self.assertEqual(sublist(list(range(3, 200, 3)), list(range(15, 200, 15))), UNEQUAL) diff --git a/evaluator/datasets/polyglot_py_unpatched/sum-of-multiples/solution.py b/evaluator/datasets/polyglot_py_unpatched/sum-of-multiples/solution.py index 55e168227..41f3a685f 100644 --- a/evaluator/datasets/polyglot_py_unpatched/sum-of-multiples/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/sum-of-multiples/solution.py @@ -1,5 +1,2 @@ def sum_of_multiples(limit, multiples): - return sum(value for value in range(limit) - if any(value % multiple == 0 - for multiple in multiples - if multiple > 0)) + return sum(value for value in range(limit) if any(value % multiple == 0 for multiple in multiples if multiple > 0)) diff --git a/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/solution.py b/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/solution.py index 9f411819a..f36f39c21 100644 --- a/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/solution.py @@ -4,54 +4,46 @@ def delivery_date(start, description): start_date = datetime.fromisoformat(start) - - if description == 'NOW': + if description == "NOW": due_date = start_date + timedelta(hours=2) - if description == 'ASAP': - if str(start_date.time()) < '13:00:00': + if description == "ASAP": + if str(start_date.time()) < "13:00:00": due_date = start_date.replace(hour=17, minute=0) else: - due_date = ( - start_date.replace(hour=13, minute=0) + - timedelta(days=1) - ) + due_date = start_date.replace(hour=13, minute=0) + timedelta(days=1) - if description =='EOW': + if description == "EOW": if start_date.isoweekday() < 4: - due_date = ( - start_date.replace(hour=17, minute=0) + - timedelta(days=5 - start_date.isoweekday()) - ) + due_date = start_date.replace(hour=17, minute=0) + timedelta(days=5 - start_date.isoweekday()) else: - due_date = ( - start_date.replace(hour=20, minute=0) + - timedelta(days=7 - start_date.isoweekday()) - ) + due_date = start_date.replace(hour=20, minute=0) + timedelta(days=7 - start_date.isoweekday()) - if description.endswith('M'): + if description.endswith("M"): month = int(description[:-1]) target = datetime(start_date.year, month, 1, 8, 0, 0) if start_date.month >= target.month: target = target.replace(year=target.year + 1) - if target.isoweekday() not in (6,7) and target.day in range(1, 8): + if target.isoweekday() not in (6, 7) and target.day in range(1, 8): due_date = target else: - if target.isoweekday() == 6: due_date = target + timedelta(days = 2) - if target.isoweekday() == 7: due_date = target + timedelta(days = 1) + if target.isoweekday() == 6: + due_date = target + timedelta(days=2) + if target.isoweekday() == 7: + due_date = target + timedelta(days=1) - if description.startswith('Q'): + if description.startswith("Q"): target = int(description[1:]) - current = ((start_date.month + 2) // 3) - month = {"Q1":4,"Q2": 7,"Q3": 10,"Q4": 1}[description] + current = (start_date.month + 2) // 3 + month = {"Q1": 4, "Q2": 7, "Q3": 10, "Q4": 1}[description] rollover = 1 if (current > target or target == 4) else 0 - due_date = start_date.replace( - start_date.year + rollover, month, 1, 8, 0, 0 - ) - timedelta(days=1) + due_date = start_date.replace(start_date.year + rollover, month, 1, 8, 0, 0) - timedelta(days=1) - if due_date.isoweekday() == 6: due_date -= timedelta(days=1) - if due_date.isoweekday() == 7: due_date -= timedelta(days=2) + if due_date.isoweekday() == 6: + due_date -= timedelta(days=1) + if due_date.isoweekday() == 7: + due_date -= timedelta(days=2) return due_date.isoformat() diff --git a/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/tests.py b/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/tests.py index 384af015b..3f115fad7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/swift-scheduling/tests.py @@ -11,99 +11,67 @@ class SwiftSchedulingTest(unittest.TestCase): def test_now_translates_to_two_hours_later(self): - self.assertEqual( - delivery_date("2012-02-13T09:00:00", "NOW"), "2012-02-13T11:00:00" - ) + self.assertEqual(delivery_date("2012-02-13T09:00:00", "NOW"), "2012-02-13T11:00:00") def test_asap_before_one_in_the_afternoon_translates_to_today_at_five_in_the_afternoon( self, ): - self.assertEqual( - delivery_date("1999-06-03T09:45:00", "ASAP"), "1999-06-03T17:00:00" - ) + self.assertEqual(delivery_date("1999-06-03T09:45:00", "ASAP"), "1999-06-03T17:00:00") def test_asap_at_one_in_the_afternoon_translates_to_tomorrow_at_one_in_the_afternoon( self, ): - self.assertEqual( - delivery_date("2008-12-21T13:00:00", "ASAP"), "2008-12-22T13:00:00" - ) + self.assertEqual(delivery_date("2008-12-21T13:00:00", "ASAP"), "2008-12-22T13:00:00") def test_asap_after_one_in_the_afternoon_translates_to_tomorrow_at_one_in_the_afternoon( self, ): - self.assertEqual( - delivery_date("2008-12-21T14:50:00", "ASAP"), "2008-12-22T13:00:00" - ) + self.assertEqual(delivery_date("2008-12-21T14:50:00", "ASAP"), "2008-12-22T13:00:00") def test_eow_on_monday_translates_to_friday_at_five_in_the_afternoon(self): - self.assertEqual( - delivery_date("2025-02-03T16:00:00", "EOW"), "2025-02-07T17:00:00" - ) + self.assertEqual(delivery_date("2025-02-03T16:00:00", "EOW"), "2025-02-07T17:00:00") def test_eow_on_tuesday_translates_to_friday_at_five_in_the_afternoon(self): - self.assertEqual( - delivery_date("1997-04-29T10:50:00", "EOW"), "1997-05-02T17:00:00" - ) + self.assertEqual(delivery_date("1997-04-29T10:50:00", "EOW"), "1997-05-02T17:00:00") def test_eow_on_wednesday_translates_to_friday_at_five_in_the_afternoon(self): - self.assertEqual( - delivery_date("2005-09-14T11:00:00", "EOW"), "2005-09-16T17:00:00" - ) + self.assertEqual(delivery_date("2005-09-14T11:00:00", "EOW"), "2005-09-16T17:00:00") def test_eow_on_thursday_translates_to_sunday_at_eight_in_the_evening(self): - self.assertEqual( - delivery_date("2011-05-19T08:30:00", "EOW"), "2011-05-22T20:00:00" - ) + self.assertEqual(delivery_date("2011-05-19T08:30:00", "EOW"), "2011-05-22T20:00:00") def test_eow_on_friday_translates_to_sunday_at_eight_in_the_evening(self): - self.assertEqual( - delivery_date("2022-08-05T14:00:00", "EOW"), "2022-08-07T20:00:00" - ) + self.assertEqual(delivery_date("2022-08-05T14:00:00", "EOW"), "2022-08-07T20:00:00") def test_eow_translates_to_leap_day(self): - self.assertEqual( - delivery_date("2008-02-25T10:30:00", "EOW"), "2008-02-29T17:00:00" - ) + self.assertEqual(delivery_date("2008-02-25T10:30:00", "EOW"), "2008-02-29T17:00:00") def test_2_m_before_the_second_month_of_this_year_translates_to_the_first_workday_of_the_second_month_of_this_year( self, ): - self.assertEqual( - delivery_date("2007-01-02T14:15:00", "2M"), "2007-02-01T08:00:00" - ) + self.assertEqual(delivery_date("2007-01-02T14:15:00", "2M"), "2007-02-01T08:00:00") def test_11_m_in_the_eleventh_month_translates_to_the_first_workday_of_the_eleventh_month_of_next_year( self, ): - self.assertEqual( - delivery_date("2013-11-21T15:30:00", "11M"), "2014-11-03T08:00:00" - ) + self.assertEqual(delivery_date("2013-11-21T15:30:00", "11M"), "2014-11-03T08:00:00") def test_4_m_in_the_ninth_month_translates_to_the_first_workday_of_the_fourth_month_of_next_year( self, ): - self.assertEqual( - delivery_date("2019-11-18T15:15:00", "4M"), "2020-04-01T08:00:00" - ) + self.assertEqual(delivery_date("2019-11-18T15:15:00", "4M"), "2020-04-01T08:00:00") def test_q1_in_the_first_quarter_translates_to_the_last_workday_of_the_first_quarter_of_this_year( self, ): - self.assertEqual( - delivery_date("2003-01-01T10:45:00", "Q1"), "2003-03-31T08:00:00" - ) + self.assertEqual(delivery_date("2003-01-01T10:45:00", "Q1"), "2003-03-31T08:00:00") def test_q4_in_the_second_quarter_translates_to_the_last_workday_of_the_fourth_quarter_of_this_year( self, ): - self.assertEqual( - delivery_date("2001-04-09T09:00:00", "Q4"), "2001-12-31T08:00:00" - ) + self.assertEqual(delivery_date("2001-04-09T09:00:00", "Q4"), "2001-12-31T08:00:00") def test_q3_in_the_fourth_quarter_translates_to_the_last_workday_of_the_third_quarter_of_next_year( self, ): - self.assertEqual( - delivery_date("2022-10-06T11:00:00", "Q3"), "2023-09-29T08:00:00" - ) + self.assertEqual(delivery_date("2022-10-06T11:00:00", "Q3"), "2023-09-29T08:00:00") diff --git a/evaluator/datasets/polyglot_py_unpatched/tournament/solution.py b/evaluator/datasets/polyglot_py_unpatched/tournament/solution.py index e711468e2..c26f961a5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/tournament/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/tournament/solution.py @@ -12,7 +12,7 @@ def invert_result(result): def parse_game(game_line): - game = game_line.split(';') + game = game_line.split(";") if len(game) == 3 and game[2] in RESULTS: result = RESULTS[game[2]] return (game[0], result), (game[1], invert_result(result)) @@ -24,13 +24,11 @@ def calculate_points(stats): def format_table(results): - table = ['Team | MP | W | D | L | P'] + table = ["Team | MP | W | D | L | P"] - for team, games in sorted( - results.items(), key=lambda group: (-calculate_points(group[1]), group[0])): - team_fmt = '{0:30} | {1:2} | {3:2} | {4:2} | {5:2} | {2:2}' - table.append( - team_fmt.format(team, sum(games), calculate_points(games), *games)) + for team, games in sorted(results.items(), key=lambda group: (-calculate_points(group[1]), group[0])): + team_fmt = "{0:30} | {1:2} | {3:2} | {4:2} | {5:2} | {2:2}" + table.append(team_fmt.format(team, sum(games), calculate_points(games), *games)) return table diff --git a/evaluator/datasets/polyglot_py_unpatched/transpose/solution.py b/evaluator/datasets/polyglot_py_unpatched/transpose/solution.py index cbb8e5f96..fb35092f7 100644 --- a/evaluator/datasets/polyglot_py_unpatched/transpose/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/transpose/solution.py @@ -1,6 +1,6 @@ def transpose(lines): - rows = [row.replace(' ', '_') for row in lines.splitlines()] + rows = [row.replace(" ", "_") for row in lines.splitlines()] rows = [row.ljust(len(max(rows, key=len))) for row in rows] - rows = [''.join(row) for row in zip(*rows)] - rows = [row.rstrip().replace('_', ' ') for row in rows] - return '\n'.join(rows) + rows = ["".join(row) for row in zip(*rows)] + rows = [row.rstrip().replace("_", " ") for row in rows] + return "\n".join(rows) diff --git a/evaluator/datasets/polyglot_py_unpatched/tree-building/main.py b/evaluator/datasets/polyglot_py_unpatched/tree-building/main.py index 75082a66b..aae5ba38d 100644 --- a/evaluator/datasets/polyglot_py_unpatched/tree-building/main.py +++ b/evaluator/datasets/polyglot_py_unpatched/tree-building/main.py @@ -16,9 +16,9 @@ def BuildTree(records): ordered_id = [i.record_id for i in records] if records: if ordered_id[-1] != len(ordered_id) - 1: - raise ValueError('broken tree') + raise ValueError("broken tree") if ordered_id[0] != 0: - raise ValueError('invalid') + raise ValueError("invalid") trees = [] parent = {} for i in range(len(ordered_id)): @@ -26,12 +26,12 @@ def BuildTree(records): if ordered_id[i] == j.record_id: if j.record_id == 0: if j.parent_id != 0: - raise ValueError('error!') + raise ValueError("error!") if j.record_id < j.parent_id: - raise ValueError('something went wrong!') + raise ValueError("something went wrong!") if j.record_id == j.parent_id: if j.record_id != 0: - raise ValueError('error!') + raise ValueError("error!") trees.append(Node(ordered_id[i])) for i in range(len(ordered_id)): for j in trees: diff --git a/evaluator/datasets/polyglot_py_unpatched/tree-building/solution.py b/evaluator/datasets/polyglot_py_unpatched/tree-building/solution.py index 7cf8a6ea9..bb1df0025 100644 --- a/evaluator/datasets/polyglot_py_unpatched/tree-building/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/tree-building/solution.py @@ -15,7 +15,7 @@ def __init__(self, node_id): def validate_record(record): if record.equal_id() and record.record_id != 0: - raise ValueError('Only root should have equal record and parent id.') + raise ValueError("Only root should have equal record and parent id.") if not record.equal_id() and record.parent_id >= record.record_id: raise ValueError("Node parent_id should be smaller than its record_id.") @@ -36,7 +36,7 @@ def BuildTree(records): for index, record_id in enumerate(ordered_id): if index != record_id: - raise ValueError('Record id is invalid or out of order.') + raise ValueError("Record id is invalid or out of order.") if record_id == root_id: root = node_dict[record_id] diff --git a/evaluator/datasets/polyglot_py_unpatched/tree-building/tests.py b/evaluator/datasets/polyglot_py_unpatched/tree-building/tests.py index 22278ef16..b2cbc4ba2 100644 --- a/evaluator/datasets/polyglot_py_unpatched/tree-building/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/tree-building/tests.py @@ -1,14 +1,14 @@ import unittest -from main import Record, BuildTree +from main import BuildTree, Record class TreeBuildingTest(unittest.TestCase): """ - Record(record_id, parent_id): records given to be processed - Node(node_id): Node in tree - BuildTree(records): records as argument and returns tree - BuildTree should raise ValueError if given records are invalid + Record(record_id, parent_id): records given to be processed + Node(node_id): Node in tree + BuildTree(records): records as argument and returns tree + BuildTree should raise ValueError if given records are invalid """ def test_empty_list_input(self): @@ -17,19 +17,13 @@ def test_empty_list_input(self): self.assertIsNone(root) def test_one_node(self): - records = [ - Record(0, 0) - ] + records = [Record(0, 0)] root = BuildTree(records) self.assert_node_is_leaf(root, node_id=0) def test_three_nodes_in_order(self): - records = [ - Record(0, 0), - Record(1, 0), - Record(2, 0) - ] + records = [Record(0, 0), Record(1, 0), Record(2, 0)] root = BuildTree(records) self.assert_node_is_branch(root, node_id=0, children_count=2) @@ -37,11 +31,7 @@ def test_three_nodes_in_order(self): self.assert_node_is_leaf(root.children[1], node_id=2) def test_three_nodes_in_reverse_order(self): - records = [ - Record(2, 0), - Record(1, 0), - Record(0, 0) - ] + records = [Record(2, 0), Record(1, 0), Record(0, 0)] root = BuildTree(records) self.assert_node_is_branch(root, node_id=0, children_count=2) @@ -49,12 +39,7 @@ def test_three_nodes_in_reverse_order(self): self.assert_node_is_leaf(root.children[1], node_id=2) def test_more_than_two_children(self): - records = [ - Record(0, 0), - Record(1, 0), - Record(2, 0), - Record(3, 0) - ] + records = [Record(0, 0), Record(1, 0), Record(2, 0), Record(3, 0)] root = BuildTree(records) self.assert_node_is_branch(root, node_id=0, children_count=3) @@ -63,15 +48,7 @@ def test_more_than_two_children(self): self.assert_node_is_leaf(root.children[2], node_id=3) def test_binary_tree(self): - records = [ - Record(6, 2), - Record(0, 0), - Record(3, 1), - Record(2, 0), - Record(4, 1), - Record(5, 2), - Record(1, 0) - ] + records = [Record(6, 2), Record(0, 0), Record(3, 1), Record(2, 0), Record(4, 1), Record(5, 2), Record(1, 0)] root = BuildTree(records) self.assert_node_is_branch(root, 0, 2) @@ -103,10 +80,7 @@ def test_unbalanced_tree(self): self.assert_node_is_leaf(root.children[1].children[0], 6) def test_root_node_has_parent(self): - records = [ - Record(0, 1), - Record(1, 0) - ] + records = [Record(0, 1), Record(1, 0)] # Root parent_id should be equal to record_id(0) with self.assertRaises(ValueError) as err: BuildTree(records) @@ -114,10 +88,7 @@ def test_root_node_has_parent(self): self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than its record_id.") def test_no_root_node(self): - records = [ - Record(1, 0), - Record(2, 0) - ] + records = [Record(1, 0), Record(2, 0)] # Record with record_id 0 (root) is missing with self.assertRaises(ValueError) as err: BuildTree(records) @@ -125,12 +96,7 @@ def test_no_root_node(self): self.assertEqual(err.exception.args[0], "Record id is invalid or out of order.") def test_non_continuous(self): - records = [ - Record(2, 0), - Record(4, 2), - Record(1, 0), - Record(0, 0) - ] + records = [Record(2, 0), Record(4, 2), Record(1, 0), Record(0, 0)] # Record with record_id 3 is missing with self.assertRaises(ValueError) as err: BuildTree(records) @@ -138,15 +104,7 @@ def test_non_continuous(self): self.assertEqual(err.exception.args[0], "Record id is invalid or out of order.") def test_cycle_directly(self): - records = [ - Record(5, 2), - Record(3, 2), - Record(2, 2), - Record(4, 1), - Record(1, 0), - Record(0, 0), - Record(6, 3) - ] + records = [Record(5, 2), Record(3, 2), Record(2, 2), Record(4, 1), Record(1, 0), Record(0, 0), Record(6, 3)] # Cycle caused by Record 2 with parent_id pointing to itself with self.assertRaises(ValueError) as err: BuildTree(records) @@ -154,15 +112,7 @@ def test_cycle_directly(self): self.assertEqual(err.exception.args[0], "Only root should have equal record and parent id.") def test_cycle_indirectly(self): - records = [ - Record(5, 2), - Record(3, 2), - Record(2, 6), - Record(4, 1), - Record(1, 0), - Record(0, 0), - Record(6, 3) - ] + records = [Record(5, 2), Record(3, 2), Record(2, 6), Record(4, 1), Record(1, 0), Record(0, 0), Record(6, 3)] # Cycle caused by Record 2 with parent_id(6) greater than record_id(2) with self.assertRaises(ValueError) as err: BuildTree(records) @@ -170,11 +120,7 @@ def test_cycle_indirectly(self): self.assertEqual(err.exception.args[0], "Node parent_id should be smaller than its record_id.") def test_higher_id_parent_of_lower_id(self): - records = [ - Record(0, 0), - Record(2, 0), - Record(1, 2) - ] + records = [Record(0, 0), Record(2, 0), Record(1, 2)] # Record 1 have parent_id(2) greater than record_id(1) with self.assertRaises(ValueError) as err: BuildTree(records) diff --git a/evaluator/datasets/polyglot_py_unpatched/triangle/solution.py b/evaluator/datasets/polyglot_py_unpatched/triangle/solution.py index 10c064649..4636187c5 100644 --- a/evaluator/datasets/polyglot_py_unpatched/triangle/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/triangle/solution.py @@ -1,7 +1,5 @@ def valid(sides): - return sum(sorted(sides)[:2]) >= sorted(sides)[2] and all( - side > 0 for side in sides - ) + return sum(sorted(sides)[:2]) >= sorted(sides)[2] and all(side > 0 for side in sides) def equilateral(sides): @@ -9,9 +7,7 @@ def equilateral(sides): def isosceles(sides): - return valid(sides) and any( - side_1 == side_2 for side_1, side_2 in zip(sorted(sides), sorted(sides)[1:]) - ) + return valid(sides) and any(side_1 == side_2 for side_1, side_2 in zip(sorted(sides), sorted(sides)[1:])) def scalene(sides): diff --git a/evaluator/datasets/polyglot_py_unpatched/trinary/solution.py b/evaluator/datasets/polyglot_py_unpatched/trinary/solution.py index 48074c008..c497c8ed4 100644 --- a/evaluator/datasets/polyglot_py_unpatched/trinary/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/trinary/solution.py @@ -2,6 +2,6 @@ def trinary(string): - if set(string) - set('012'): + if set(string) - set("012"): return 0 return reduce(lambda idx, edx: idx * 3 + int(edx), string, 0) diff --git a/evaluator/datasets/polyglot_py_unpatched/trinary/tests.py b/evaluator/datasets/polyglot_py_unpatched/trinary/tests.py index 3e92d2d01..4881d500a 100644 --- a/evaluator/datasets/polyglot_py_unpatched/trinary/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/trinary/tests.py @@ -5,26 +5,26 @@ class TrinaryTest(unittest.TestCase): def test_valid_trinary1(self): - self.assertEqual(trinary('0'), 0) + self.assertEqual(trinary("0"), 0) def test_valid_trinary2(self): - self.assertEqual(trinary('1'), 1) + self.assertEqual(trinary("1"), 1) def test_valid_trinary3(self): - self.assertEqual(trinary('10'), 3) + self.assertEqual(trinary("10"), 3) def test_valid_trinary4(self): - self.assertEqual(trinary('102101'), 307) + self.assertEqual(trinary("102101"), 307) def test_valid_trinary5(self): - self.assertEqual(trinary('22222'), 242) + self.assertEqual(trinary("22222"), 242) def test_valid_trinary6(self): - self.assertEqual(trinary('10000'), 81) + self.assertEqual(trinary("10000"), 81) def test_invalid_trinary(self): - self.assertEqual(trinary('13201'), 0) + self.assertEqual(trinary("13201"), 0) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/evaluator/datasets/polyglot_py_unpatched/twelve-days/solution.py b/evaluator/datasets/polyglot_py_unpatched/twelve-days/solution.py index 6b24c65b1..e7a2054e3 100644 --- a/evaluator/datasets/polyglot_py_unpatched/twelve-days/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/twelve-days/solution.py @@ -1,28 +1,43 @@ -GIFTS = ['twelve Drummers Drumming', - 'eleven Pipers Piping', - 'ten Lords-a-Leaping', - 'nine Ladies Dancing', - 'eight Maids-a-Milking', - 'seven Swans-a-Swimming', - 'six Geese-a-Laying', - 'five Gold Rings', - 'four Calling Birds', - 'three French Hens', - 'two Turtle Doves', - 'a Partridge in a Pear Tree'] +GIFTS = [ + "twelve Drummers Drumming", + "eleven Pipers Piping", + "ten Lords-a-Leaping", + "nine Ladies Dancing", + "eight Maids-a-Milking", + "seven Swans-a-Swimming", + "six Geese-a-Laying", + "five Gold Rings", + "four Calling Birds", + "three French Hens", + "two Turtle Doves", + "a Partridge in a Pear Tree", +] -ORDINAL = [None, 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', - 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth'] +ORDINAL = [ + None, + "first", + "second", + "third", + "fourth", + "fifth", + "sixth", + "seventh", + "eighth", + "ninth", + "tenth", + "eleventh", + "twelfth", +] def verse(day_number): gifts = GIFTS[-day_number:] if len(gifts) > 1: - gifts[:-1] = [', '.join(gifts[:-1])] + gifts[:-1] = [", ".join(gifts[:-1])] - gifts = ', and '.join(gifts) - return f'On the {ORDINAL[day_number]} day of Christmas my true love gave to me: {gifts}.' + gifts = ", and ".join(gifts) + return f"On the {ORDINAL[day_number]} day of Christmas my true love gave to me: {gifts}." def recite(start, end): diff --git a/evaluator/datasets/polyglot_py_unpatched/twelve-days/tests.py b/evaluator/datasets/polyglot_py_unpatched/twelve-days/tests.py index e606bf22b..2d11b5158 100644 --- a/evaluator/datasets/polyglot_py_unpatched/twelve-days/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/twelve-days/tests.py @@ -15,17 +15,12 @@ class TwelveDaysTest(unittest.TestCase): def test_first_day_a_partridge_in_a_pear_tree(self): - expected = [ - "On the first day of Christmas my true love gave to me: " - "a Partridge in a Pear Tree." - ] + expected = ["On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree."] self.assertEqual(recite(1, 1), expected) def test_second_day_two_turtle_doves(self): expected = [ - "On the second day of Christmas my true love gave to me: " - "two Turtle Doves, " - "and a Partridge in a Pear Tree." + "On the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree." ] self.assertEqual(recite(2, 2), expected) diff --git a/evaluator/datasets/polyglot_py_unpatched/two-bucket/solution.py b/evaluator/datasets/polyglot_py_unpatched/two-bucket/solution.py index 83077eb16..6fa8ececd 100644 --- a/evaluator/datasets/polyglot_py_unpatched/two-bucket/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/two-bucket/solution.py @@ -1,13 +1,13 @@ -''' - This solution implements a breadth-first search of the graph - of possible valid states for the two buckets until it reaches a state - in which one of the two buckets contains the goal amount -''' +""" +This solution implements a breadth-first search of the graph +of possible valid states for the two buckets until it reaches a state +in which one of the two buckets contains the goal amount +""" def measure(bucket_one, bucket_two, goal, start_bucket): sizes = [bucket_one, bucket_two] - goal_index = 0 if start_bucket == 'one' else 1 + goal_index = 0 if start_bucket == "one" else 1 def empty(buckets, idx): return [0, buckets[1]] if idx == 0 else [buckets[0], 0] @@ -22,7 +22,7 @@ def consolidate(buckets, idx): return [target, source] if idx == 0 else [source, target] def bucket_str(buckets): - return f'{buckets[0]},{buckets[1]}' + return f"{buckets[0]},{buckets[1]}" invalid = [0, 0] invalid[1 - goal_index] = sizes[1 - goal_index] @@ -44,10 +44,10 @@ def bucket_str(buckets): to_visit.append((fill(buckets, idx), number_count)) to_visit.append((consolidate(buckets, idx), number_count)) if not any(to_visit): - raise ValueError('No more moves!') + raise ValueError("No more moves!") buckets, count = to_visit.pop(0) goal_index = buckets.index(goal) - goal_bucket = ['one', 'two'][goal_index] + goal_bucket = ["one", "two"][goal_index] other_bucket = buckets[1 - goal_index] return (count, goal_bucket, other_bucket) diff --git a/evaluator/datasets/polyglot_py_unpatched/two-fer/solution.py b/evaluator/datasets/polyglot_py_unpatched/two-fer/solution.py index 425d236d2..95d3c99cf 100644 --- a/evaluator/datasets/polyglot_py_unpatched/two-fer/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/two-fer/solution.py @@ -1,2 +1,2 @@ -def two_fer(name='you'): - return f'One for {name}, one for me.' +def two_fer(name="you"): + return f"One for {name}, one for me." diff --git a/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/solution.py b/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/solution.py index ed5a393b6..0f6a2f43b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/solution.py @@ -1,5 +1,5 @@ EIGHT_BIT_MASK = 0x80 -SEVEN_BIT_MASK = 0x7f +SEVEN_BIT_MASK = 0x7F def encode_single(number): @@ -23,12 +23,12 @@ def decode(byte_string): for idx, byte in enumerate(byte_string): number <<= 7 - number += (byte & SEVEN_BIT_MASK) + number += byte & SEVEN_BIT_MASK if byte & EIGHT_BIT_MASK == 0: values.append(number) number = 0 elif idx == len(byte_string) - 1: - raise ValueError('incomplete sequence') + raise ValueError("incomplete sequence") return values diff --git a/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/tests.py b/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/tests.py index 75ff06e97..bd1764bfe 100644 --- a/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/variable-length-quantity/tests.py @@ -75,9 +75,7 @@ def test_two_single_byte_values(self): self.assertEqual(encode([0x40, 0x7F]), [0x40, 0x7F]) def test_two_multi_byte_values(self): - self.assertEqual( - encode([0x4000, 0x123456]), [0x81, 0x80, 0x0, 0xC8, 0xE8, 0x56] - ) + self.assertEqual(encode([0x4000, 0x123456]), [0x81, 0x80, 0x0, 0xC8, 0xE8, 0x56]) def test_many_multi_byte_values(self): self.assertEqual( diff --git a/evaluator/datasets/polyglot_py_unpatched/word-count/solution.py b/evaluator/datasets/polyglot_py_unpatched/word-count/solution.py index 03fb2608a..d7c710c7b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/word-count/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/word-count/solution.py @@ -1,7 +1,6 @@ import re from collections import Counter - WORDS = re.compile("[a-z0-9]+(['][a-z]+)?") diff --git a/evaluator/datasets/polyglot_py_unpatched/word-count/tests.py b/evaluator/datasets/polyglot_py_unpatched/word-count/tests.py index 38d0d08fa..52796e9fd 100644 --- a/evaluator/datasets/polyglot_py_unpatched/word-count/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/word-count/tests.py @@ -26,9 +26,7 @@ def test_handles_cramped_lists(self): self.assertEqual(count_words("one,two,three"), {"one": 1, "two": 1, "three": 1}) def test_handles_expanded_lists(self): - self.assertEqual( - count_words("one,\ntwo,\nthree"), {"one": 1, "two": 1, "three": 1} - ) + self.assertEqual(count_words("one,\ntwo,\nthree"), {"one": 1, "two": 1, "three": 1}) def test_ignore_punctuation(self): self.assertEqual( @@ -37,9 +35,7 @@ def test_ignore_punctuation(self): ) def test_include_numbers(self): - self.assertEqual( - count_words("testing, 1, 2 testing"), {"testing": 2, "1": 1, "2": 1} - ) + self.assertEqual(count_words("testing, 1, 2 testing"), {"testing": 2, "1": 1, "2": 1}) def test_normalize_case(self): self.assertEqual(count_words("go Go GO Stop stop"), {"go": 3, "stop": 2}) @@ -81,14 +77,10 @@ def test_substrings_from_the_beginning(self): ) def test_multiple_spaces_not_detected_as_a_word(self): - self.assertEqual( - count_words(" multiple whitespaces"), {"multiple": 1, "whitespaces": 1} - ) + self.assertEqual(count_words(" multiple whitespaces"), {"multiple": 1, "whitespaces": 1}) def test_alternating_word_separators_not_detected_as_a_word(self): - self.assertEqual( - count_words(",\n,one,\n ,two \n 'three'"), {"one": 1, "two": 1, "three": 1} - ) + self.assertEqual(count_words(",\n,one,\n ,two \n 'three'"), {"one": 1, "two": 1, "three": 1}) def test_quotation_for_word_with_apostrophe(self): self.assertEqual(count_words("can, can't, 'can't'"), {"can": 1, "can't": 2}) @@ -97,9 +89,7 @@ def test_quotation_for_word_with_apostrophe(self): def test_tabs(self): self.assertEqual( - count_words( - "rah rah ah ah ah roma roma ma ga ga oh la la want your bad romance" - ), + count_words("rah rah ah ah ah roma roma ma ga ga oh la la want your bad romance"), { "rah": 2, "ah": 3, diff --git a/evaluator/datasets/polyglot_py_unpatched/word-search/solution.py b/evaluator/datasets/polyglot_py_unpatched/word-search/solution.py index fe2b9b665..f61487133 100644 --- a/evaluator/datasets/polyglot_py_unpatched/word-search/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/word-search/solution.py @@ -7,7 +7,7 @@ def __init__(self, x, y): self.y = y def __repr__(self): - return f'Point({self.x}:{self.y})' + return f"Point({self.x}:{self.y})" def __add__(self, other): return Point(self.x + other.x, self.y + other.y) @@ -22,8 +22,16 @@ def __ne__(self, other): return not self == other -DIRECTIONS = (Point(1, 0), Point(1, -1), Point(1, 1), Point(-1, -1), - Point(0, -1), Point(0, 1), Point(-1, 1), Point(-1, 0)) +DIRECTIONS = ( + Point(1, 0), + Point(1, -1), + Point(1, 1), + Point(-1, -1), + Point(0, -1), + Point(0, 1), + Point(-1, 1), + Point(-1, 0), +) class WordSearch: @@ -48,8 +56,7 @@ def find(self, word, position, direction): return position, current - direction def search(self, word): - positions = (Point(idx, edx) - for idx in range(self.width) for edx in range(self.height)) + positions = (Point(idx, edx) for idx in range(self.width) for edx in range(self.height)) for position in positions: for direction in DIRECTIONS: result = self.find(word, position, direction) diff --git a/evaluator/datasets/polyglot_py_unpatched/word-search/tests.py b/evaluator/datasets/polyglot_py_unpatched/word-search/tests.py index 0d2838a6f..8d0f650e4 100644 --- a/evaluator/datasets/polyglot_py_unpatched/word-search/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/word-search/tests.py @@ -5,8 +5,8 @@ import unittest from main import ( - WordSearch, Point, + WordSearch, ) diff --git a/evaluator/datasets/polyglot_py_unpatched/wordy/solution.py b/evaluator/datasets/polyglot_py_unpatched/wordy/solution.py index 2488153e8..484535783 100644 --- a/evaluator/datasets/polyglot_py_unpatched/wordy/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/wordy/solution.py @@ -1,16 +1,15 @@ from operator import add, mul, sub from operator import floordiv as div - -VALID_OPERATIONS = {'plus': add, 'minus': sub, 'multiplied by': mul, 'divided by': div} +VALID_OPERATIONS = {"plus": add, "minus": sub, "multiplied by": mul, "divided by": div} def answer(question): if not bool(question[8:-1].strip().lower().split()): - raise ValueError('syntax error') + raise ValueError("syntax error") - elif not question.startswith('What is '): - raise ValueError('unknown operation') + elif not question.startswith("What is "): + raise ValueError("unknown operation") else: words = question[8:-1].strip().lower().split() @@ -19,7 +18,7 @@ def answer(question): try: main_value = int(words.pop()) except ValueError as error: - raise ValueError('syntax error') from error + raise ValueError("syntax error") from error while words: operation = [words.pop()] @@ -30,20 +29,20 @@ def answer(question): break except ValueError as error: if next_to_evaluate == operation[-1]: - raise ValueError('syntax error') from error + raise ValueError("syntax error") from error else: operation.append(next_to_evaluate) else: - if operation[-1] not in VALID_OPERATIONS and not operation[-1].isdigit() : - raise ValueError('unknown operation') + if operation[-1] not in VALID_OPERATIONS and not operation[-1].isdigit(): + raise ValueError("unknown operation") else: - raise ValueError('syntax error') + raise ValueError("syntax error") - operation = ' '.join(operation) + operation = " ".join(operation) try: main_value = VALID_OPERATIONS[operation](main_value, second_value) except KeyError as error: - raise ValueError('syntax error') from error + raise ValueError("syntax error") from error return main_value diff --git a/evaluator/datasets/polyglot_py_unpatched/yacht/solution.py b/evaluator/datasets/polyglot_py_unpatched/yacht/solution.py index dedcb7ac8..a290caf97 100644 --- a/evaluator/datasets/polyglot_py_unpatched/yacht/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/yacht/solution.py @@ -62,4 +62,4 @@ def score(dice, category): try: return functions[category](dice) except IndexError as error: - raise ValueError('No such category.') from error + raise ValueError("No such category.") from error diff --git a/evaluator/datasets/polyglot_py_unpatched/yacht/tests.py b/evaluator/datasets/polyglot_py_unpatched/yacht/tests.py index 2a587b17d..864de05cd 100644 --- a/evaluator/datasets/polyglot_py_unpatched/yacht/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/yacht/tests.py @@ -3,6 +3,7 @@ # File last updated on 2023-07-19 import unittest + import main as yacht diff --git a/evaluator/datasets/polyglot_py_unpatched/zebra-puzzle/solution.py b/evaluator/datasets/polyglot_py_unpatched/zebra-puzzle/solution.py index a6ea8f937..f9385124b 100644 --- a/evaluator/datasets/polyglot_py_unpatched/zebra-puzzle/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/zebra-puzzle/solution.py @@ -25,25 +25,36 @@ def solution(): # - J08K <3 (1:05 AM, nov 29th, 2021) result = next( - [{ - english_man: 'Englishman', - spaniard: 'Spaniard', - ukrainian: 'Ukrainian', - japanese: 'Japanese', - norwegian: 'Norwegian' - }[idx] for idx in (water, zebra)] + [ + { + english_man: "Englishman", + spaniard: "Spaniard", + ukrainian: "Ukrainian", + japanese: "Japanese", + norwegian: "Norwegian", + }[idx] + for idx in (water, zebra) + ] for (red, green, ivory, yellow, blue) in orderings if just_right_of(green, ivory) for (english_man, spaniard, ukrainian, japanese, norwegian) in orderings - if english_man is red if norwegian is first if next_to(norwegian, blue) - for (coffee, tea, milk, orange_juice, water) in orderings if coffee is green - if ukrainian is tea if milk is middle - for (old_gold, kools, chesterfields, lucky_strike, parliaments - ) in orderings if kools is yellow if lucky_strike is orange_juice + if english_man is red + if norwegian is first + if next_to(norwegian, blue) + for (coffee, tea, milk, orange_juice, water) in orderings + if coffee is green + if ukrainian is tea + if milk is middle + for (old_gold, kools, chesterfields, lucky_strike, parliaments) in orderings + if kools is yellow + if lucky_strike is orange_juice if japanese is parliaments - for (dog, snails, fox, horse, zebra) in orderings if spaniard is dog - if old_gold is snails if next_to(chesterfields, fox) - if next_to(kools, horse)) + for (dog, snails, fox, horse, zebra) in orderings + if spaniard is dog + if old_gold is snails + if next_to(chesterfields, fox) + if next_to(kools, horse) + ) return result diff --git a/evaluator/datasets/polyglot_py_unpatched/zipper/solution.py b/evaluator/datasets/polyglot_py_unpatched/zipper/solution.py index 570771ddb..8fdeb2e52 100644 --- a/evaluator/datasets/polyglot_py_unpatched/zipper/solution.py +++ b/evaluator/datasets/polyglot_py_unpatched/zipper/solution.py @@ -8,28 +8,28 @@ def __init__(self, tree, ancestors): self.ancestors = ancestors def value(self): - return self.tree['value'] + return self.tree["value"] def set_value(self, value): - self.tree['value'] = value + self.tree["value"] = value return self def left(self): - if self.tree['left'] is None: + if self.tree["left"] is None: return None - return Zipper(self.tree['left'], self.ancestors + [self.tree]) + return Zipper(self.tree["left"], self.ancestors + [self.tree]) def set_left(self, tree): - self.tree['left'] = tree + self.tree["left"] = tree return self def right(self): - if self.tree['right'] is None: + if self.tree["right"] is None: return None - return Zipper(self.tree['right'], self.ancestors + [self.tree]) + return Zipper(self.tree["right"], self.ancestors + [self.tree]) def set_right(self, tree): - self.tree['right'] = tree + self.tree["right"] = tree return self def up(self): diff --git a/evaluator/datasets/polyglot_py_unpatched/zipper/tests.py b/evaluator/datasets/polyglot_py_unpatched/zipper/tests.py index 9c7ef27ab..8bfa330ac 100644 --- a/evaluator/datasets/polyglot_py_unpatched/zipper/tests.py +++ b/evaluator/datasets/polyglot_py_unpatched/zipper/tests.py @@ -207,9 +207,7 @@ def test_set_left_with_leaf(self): } zipper = Zipper.from_tree(initial) - result = ( - zipper.left().set_left({"value": 5, "left": None, "right": None}).to_tree() - ) + result = zipper.left().set_left({"value": 5, "left": None, "right": None}).to_tree() self.assertEqual(result, expected) def test_set_right_with_null(self): diff --git a/evaluator/models.py b/evaluator/models.py index 6ee73f16f..3fa18f7a1 100644 --- a/evaluator/models.py +++ b/evaluator/models.py @@ -1,19 +1,20 @@ -import docker - from typing import Any, Dict, Optional + +import docker from pydantic import BaseModel, ConfigDict -from models.evaluation_run import EvaluationRunErrorCode +from models.evaluation_run import EvaluationRunErrorCode class Sandbox(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) # Because of docker.models.containers.Container + model_config = ConfigDict(arbitrary_types_allowed=True) # Because of docker.models.containers.Container name: str temp_dir: str container: docker.models.containers.Container timeout_seconds: Optional[int] + class SandboxResult(BaseModel): success: bool @@ -24,11 +25,11 @@ class SandboxResult(BaseModel): error: Optional[str] = None traceback: Optional[str] = None + class SandboxResultWithLogs(SandboxResult): logs: str - # Can be raised by some methods in ProblemSuite. These exceptions should be # caught and handled by the validator. # @@ -38,8 +39,10 @@ class SandboxResultWithLogs(SandboxResult): # validator. The only keys that could be in the extra field are "agent_logs" or # "eval_logs". class EvaluationRunException(Exception): - def __init__(self, error_code: EvaluationRunErrorCode, error_message: str, *, extra: Optional[Dict[str, Any]] = None): + def __init__( + self, error_code: EvaluationRunErrorCode, error_message: str, *, extra: Optional[Dict[str, Any]] = None + ): super().__init__(error_message) self.error_code = error_code self.error_message = error_message - self.extra = extra \ No newline at end of file + self.extra = extra diff --git a/evaluator/problem_suites/AGENT_RUNNER.py b/evaluator/problem_suites/AGENT_RUNNER.py index 18be5e40d..146352777 100644 --- a/evaluator/problem_suites/AGENT_RUNNER.py +++ b/evaluator/problem_suites/AGENT_RUNNER.py @@ -1,8 +1,8 @@ -import sys +import importlib.util import json +import sys import time import traceback -import importlib.util def main(): @@ -16,21 +16,21 @@ def main(): with open("/sandbox/input.json", "r") as f: input_data = json.load(f) print("[AGENT_RUNNER] Read input.json") - + # Import agent module print("[AGENT_RUNNER] Loading /sandbox/agent.py") spec = importlib.util.spec_from_file_location("agent", "/sandbox/agent.py") agent_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(agent_module) print("[AGENT_RUNNER] Loaded /sandbox/agent.py") - + # Check for the agent_main() function in /sandbox/agent.py if hasattr(agent_module, "agent_main"): print("[AGENT_RUNNER] agent_main() function found in /sandbox/agent.py") else: print("[AGENT_RUNNER] agent_main() function not found in /sandbox/agent.py") raise Exception("agent_main() function not found in /sandbox/agent.py") - + # Invoke agent_main function print("[AGENT_RUNNER] Entering agent's agent_main()") agent_main_return_value = agent_module.agent_main(input_data) @@ -40,38 +40,30 @@ def main(): if not isinstance(agent_main_return_value, str): raise Exception("agent_main() function returned a non-string value") - output = { - "success": True, - "output": agent_main_return_value - } + output = {"success": True, "output": agent_main_return_value} print("[AGENT_RUNNER] Writing output.json") with open("/sandbox/output.json", "w") as f: json.dump(output, f, indent=2) print("[AGENT_RUNNER] Wrote output.json") - + except Exception as e: print("[AGENT_RUNNER] Exception:") traceback.print_exc(file=sys.stdout) - - output = { - "success": False, - "error": str(e), - "traceback": traceback.format_exc() - } - + + output = {"success": False, "error": str(e), "traceback": traceback.format_exc()} + try: print("[AGENT_RUNNER] Writing output.json") with open("/sandbox/output.json", "w") as f: json.dump(output, f, indent=2) print("[AGENT_RUNNER] Wrote output.json") - except: - print("[AGENT_RUNNER] Failed to write output.json") + except Exception as e: + print(f"[AGENT_RUNNER] Failed to write output.json: {e}") pass print("[AGENT_RUNNER] Exiting main()") - if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/evaluator/problem_suites/polyglot/TEST_RUNNER.py b/evaluator/problem_suites/polyglot/TEST_RUNNER.py index 4449b84d0..f59ed763b 100644 --- a/evaluator/problem_suites/polyglot/TEST_RUNNER.py +++ b/evaluator/problem_suites/polyglot/TEST_RUNNER.py @@ -1,17 +1,13 @@ -import os -import sys +import importlib.util import json -import unittest +import sys import traceback -import importlib.util - - +import unittest repo_path = "/sandbox/repo" sys.path.insert(0, repo_path) - def run_tests(): # Load main module print("[POLYGLOT_TEST_RUNNER] Loading main.py") @@ -19,41 +15,39 @@ def run_tests(): main_module = importlib.util.module_from_spec(main_spec) main_spec.loader.exec_module(main_module) print("[POLYGLOT_TEST_RUNNER] Loaded main.py") - + # Load tests module print("[POLYGLOT_TEST_RUNNER] Loading tests.py") tests_spec = importlib.util.spec_from_file_location("tests", "/sandbox/repo/tests.py") tests_module = importlib.util.module_from_spec(tests_spec) tests_spec.loader.exec_module(tests_module) print("[POLYGLOT_TEST_RUNNER] Loaded tests.py") - + test_class = None for name in dir(tests_module): obj = getattr(tests_module, name) - if (isinstance(obj, type) and issubclass(obj, unittest.TestCase) and obj is not unittest.TestCase): + if isinstance(obj, type) and issubclass(obj, unittest.TestCase) and obj is not unittest.TestCase: test_class = obj break - + if not test_class: raise Exception("No test class found in tests.py") - + print(f"[POLYGLOT_TEST_RUNNER] Found test class: {test_class.__name__}") - + test_methods = [method for method in dir(test_class) if method.startswith("test_")] print(f"[POLYGLOT_TEST_RUNNER] Found {len(test_methods)} test methods") - - test_results = [] for method_name in test_methods: test_results.append({"name": method_name, "category": "default", "status": "skip"}) - + total_tests = len(test_results) test_instance = test_class() - + for test_index, test_result in enumerate(test_results, 1): method_name = test_result["name"] - + try: print(f"[POLYGLOT_TEST_RUNNER] [{test_index}/{total_tests}] Running {method_name}...") method = getattr(test_instance, method_name) @@ -64,56 +58,49 @@ def run_tests(): print(f"[POLYGLOT_TEST_RUNNER] {method_name}: FAILED - {e}") test_result["status"] = "fail" # break - - return test_results + return test_results def main(): print("[POLYGLOT_TEST_RUNNER] Entered main()") - + try: test_results = run_tests() print("[POLYGLOT_TEST_RUNNER] Test results:") print(json.dumps(test_results, indent=2)) - + tests_passed = sum(1 for test in test_results if test["status"] == "pass") tests_failed = sum(1 for test in test_results if test["status"] == "fail") tests_skipped = sum(1 for test in test_results if test["status"] == "skip") - - print(f"[POLYGLOT_TEST_RUNNER] Test summary: {tests_passed} passed, {tests_failed} failed, {tests_skipped} skipped") + + print( + f"[POLYGLOT_TEST_RUNNER] Test summary: {tests_passed} passed, {tests_failed} failed, {tests_skipped} skipped" + ) print("[POLYGLOT_TEST_RUNNER] Writing output.json") with open("/sandbox/output.json", "w") as f: - json.dump({ - "success": True, - "output": test_results - }, f, indent=2) + json.dump({"success": True, "output": test_results}, f, indent=2) print("[POLYGLOT_TEST_RUNNER] Wrote output.json") except Exception as e: print("[POLYGLOT_TEST_RUNNER] Exception:") traceback.print_exc(file=sys.stdout) - - output = { - "success": False, - "error": str(e), - "traceback": traceback.format_exc() - } - + + output = {"success": False, "error": str(e), "traceback": traceback.format_exc()} + try: print("[POLYGLOT_TEST_RUNNER] Writing output.json") with open("/sandbox/output.json", "w") as f: json.dump(output, f, indent=2) print("[POLYGLOT_TEST_RUNNER] Wrote output.json") - except: - print("[POLYGLOT_TEST_RUNNER] Failed to write output.json") + except Exception as e: + print(f"[POLYGLOT_TEST_RUNNER] Failed to write output.json: {e}") pass print("[POLYGLOT_TEST_RUNNER] Exiting main()") - if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/evaluator/problem_suites/polyglot/polyglot_suite.py b/evaluator/problem_suites/polyglot/polyglot_suite.py index bd9db98ac..8fdbf9f27 100644 --- a/evaluator/problem_suites/polyglot/polyglot_suite.py +++ b/evaluator/problem_suites/polyglot/polyglot_suite.py @@ -1,31 +1,30 @@ """The Polyglot problem suite.""" import os -import shutil import pathlib -import requests +import shutil import traceback -import utils.logger as logger - from enum import Enum -from uuid import UUID from typing import List, Tuple -from models.problem import Problem -from evaluator.models import Sandbox -from models.problem import ProblemTestResult -from evaluator.models import EvaluationRunException +from uuid import UUID + +import requests + +import utils.logger as logger +from evaluator.models import EvaluationRunException, Sandbox +from evaluator.problem_suites.problem_suite import ProblemSuite, ProblemSuiteName +from evaluator.sandbox.sandbox_manager import SandboxManager from models.evaluation_run import EvaluationRunErrorCode +from models.problem import Problem, ProblemTestResult +from utils.diff import apply_diff_to_local_repo, get_file_diff, validate_diff_for_local_repo from utils.git import init_local_repo_with_initial_commit -from evaluator.sandbox.sandbox_manager import SandboxManager -from evaluator.problem_suites.problem_suite import ProblemSuite, ProblemSuiteName -from utils.diff import get_file_diff, apply_diff_to_local_repo, validate_diff_for_local_repo - class PolyglotSuiteLanguage(str, Enum): PYTHON = "py" JAVASCRIPT = "js" + class PolyglotSuite(ProblemSuite): def __init__(self, language: PolyglotSuiteLanguage, unpatched: bool = False): self.problems = {} @@ -35,99 +34,104 @@ def __init__(self, language: PolyglotSuiteLanguage, unpatched: bool = False): self.unpatched = unpatched # /evaluator/datasets/polyglot_* - dataset_path = str(pathlib.Path(__file__).parent.parent.parent / "datasets" / (f"polyglot_{self.language}" + ("_unpatched" if unpatched else ""))) + dataset_path = str( + pathlib.Path(__file__).parent.parent.parent + / "datasets" + / (f"polyglot_{self.language}" + ("_unpatched" if unpatched else "")) + ) logger.info(f"Loading problems from {dataset_path}...") - + # Find problems problem_names = [] for entry in os.listdir(dataset_path): entry_path = os.path.join(dataset_path, entry) if os.path.isdir(entry_path): problem_names.append(entry) - + logger.debug(f"Found {len(problem_names)} problems") - + # Process each problem for problem_name in sorted(problem_names): problem_dir = os.path.join(dataset_path, problem_name) - + # Verify directory exists if not os.path.exists(problem_dir): logger.fatal(f"Problem directory not found: {problem_name}") - + # Check for required files - required_files = ["instructions.md", f"main.{self.language}", f"solution.{self.language}", f"tests.{self.language}"] + required_files = [ + "instructions.md", + f"main.{self.language}", + f"solution.{self.language}", + f"tests.{self.language}", + ] missing_files = [] - + for required_file in required_files: file_path = os.path.join(problem_dir, required_file) if not os.path.exists(file_path): missing_files.append(required_file) - + if missing_files: logger.fatal(f"Problem {problem_name} missing files: {missing_files}") - + # Read problem statement from instructions.md instructions_path = os.path.join(problem_dir, "instructions.md") with open(instructions_path, "r") as f: problem_statement = f.read() - # Calculate diff between main.* and solution.* main_path = os.path.join(problem_dir, f"main.{self.language}") solution_path = os.path.join(problem_dir, f"solution.{self.language}") solution_diff = get_file_diff(main_path, solution_path) - - # Add the problem to the suite - self._add_problem(Problem( - name=f"{problem_name}-{self.language}", + self._add_problem( + Problem( + name=f"{problem_name}-{self.language}", + problem_statement=problem_statement, + solution_diff=solution_diff, + ) + ) - problem_statement=problem_statement, - solution_diff=solution_diff - )) - logger.debug(f"Problem {problem_name} verified successfully") - - logger.info(f"Successfully loaded {len(self.problems)} problems from {dataset_path}") - + logger.info(f"Successfully loaded {len(self.problems)} problems from {dataset_path}") - def copy_problem_files_to_directory( - self, - problem: Problem, - dir: str, - *, - include_tests: bool = False - ) -> None: + def copy_problem_files_to_directory(self, problem: Problem, dir: str, *, include_tests: bool = False) -> None: # /evaluator/datasets/polyglot_*/* - problem_dir = str(pathlib.Path(__file__).parent.parent.parent / "datasets" / (f"polyglot_{self.language}" + ("_unpatched" if self.unpatched else "")) / problem.name.rsplit("-", 1)[0]) - + problem_dir = str( + pathlib.Path(__file__).parent.parent.parent + / "datasets" + / (f"polyglot_{self.language}" + ("_unpatched" if self.unpatched else "")) + / problem.name.rsplit("-", 1)[0] + ) + # Copy main.* shutil.copy2(os.path.join(problem_dir, f"main.{self.language}"), os.path.join(dir, f"main.{self.language}")) logger.debug(f"Copied main.{self.language} to {dir} for {problem.name}") if include_tests: # Copy tests.* - shutil.copy2(os.path.join(problem_dir, f"tests.{self.language}"), os.path.join(dir, f"tests.{self.language}")) + shutil.copy2( + os.path.join(problem_dir, f"tests.{self.language}"), os.path.join(dir, f"tests.{self.language}") + ) logger.debug(f"Copied tests.{self.language} to {dir} for {problem.name}") # Initialize git repository with initial commit init_local_repo_with_initial_commit(dir, "Initial commit") - - def initialize_eval_sandbox( self, sandbox_manager: SandboxManager, problem: Problem, evaluation_run_id: UUID, patch: str, - timeout_seconds: int + timeout_seconds: int, ) -> Sandbox: try: + def _on_mount(temp_dir: str): # Create /sandbox/repo directory sandbox_repo_dir = os.path.join(temp_dir, "repo") @@ -141,20 +145,18 @@ def _on_mount(temp_dir: str): if not is_valid: raise EvaluationRunException( EvaluationRunErrorCode.AGENT_INVALID_PATCH, - f"{EvaluationRunErrorCode.AGENT_INVALID_PATCH.get_error_message()}: {error_message}" + f"{EvaluationRunErrorCode.AGENT_INVALID_PATCH.get_error_message()}: {error_message}", ) # Apply the patch apply_diff_to_local_repo(patch, sandbox_repo_dir) - - return sandbox_manager.initialize_sandbox( name=f"eval-sandbox-{problem.name}-{evaluation_run_id}", script_path=os.path.join(os.path.dirname(__file__), f"TEST_RUNNER.{self.language}"), env_vars={"EVAL_TIMEOUT": str(timeout_seconds)}, on_mount=_on_mount, - timeout_seconds=timeout_seconds + timeout_seconds=timeout_seconds, ) except EvaluationRunException: @@ -163,15 +165,11 @@ def _on_mount(temp_dir: str): except Exception as e: raise EvaluationRunException( EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_EVAL, - f"{EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" + f"{EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", ) - - def run_eval_sandbox( - self, - sandbox_manager: SandboxManager, - eval_sandbox: Sandbox + self, sandbox_manager: SandboxManager, eval_sandbox: Sandbox ) -> Tuple[List[ProblemTestResult], str]: try: try: @@ -185,16 +183,18 @@ def run_eval_sandbox( if timed_out: raise EvaluationRunException( EvaluationRunErrorCode.AGENT_TIMEOUT_RUNNING_EVAL, - f"{EvaluationRunErrorCode.AGENT_TIMEOUT_RUNNING_EVAL.get_error_message()}: The agent exceeded the timeout of {eval_sandbox.timeout_seconds} seconds." + f"{EvaluationRunErrorCode.AGENT_TIMEOUT_RUNNING_EVAL.get_error_message()}: The agent exceeded the timeout of {eval_sandbox.timeout_seconds} seconds.", ) if not sandbox_result_with_logs.success: raise EvaluationRunException( EvaluationRunErrorCode.AGENT_EXCEPTION_RUNNING_EVAL, - f"{EvaluationRunErrorCode.AGENT_EXCEPTION_RUNNING_EVAL.get_error_message()}: {sandbox_result_with_logs.error}\n\nTraceback:\n{sandbox_result_with_logs.traceback}" + f"{EvaluationRunErrorCode.AGENT_EXCEPTION_RUNNING_EVAL.get_error_message()}: {sandbox_result_with_logs.error}\n\nTraceback:\n{sandbox_result_with_logs.traceback}", ) - - return [ProblemTestResult(**test) for test in sandbox_result_with_logs.output], sandbox_result_with_logs.logs + + return [ + ProblemTestResult(**test) for test in sandbox_result_with_logs.output + ], sandbox_result_with_logs.logs except EvaluationRunException: raise @@ -202,13 +202,12 @@ def run_eval_sandbox( except Exception as e: raise EvaluationRunException( EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_EVAL, - f"{EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" + f"{EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", ) - POLYGLOT_PY_SUITE = PolyglotSuite(PolyglotSuiteLanguage.PYTHON) POLYGLOT_JS_SUITE = PolyglotSuite(PolyglotSuiteLanguage.JAVASCRIPT) POLYGLOT_PY_UNPATCHED_SUITE = PolyglotSuite(PolyglotSuiteLanguage.PYTHON, unpatched=True) -POLYGLOT_JS_UNPATCHED_SUITE = PolyglotSuite(PolyglotSuiteLanguage.JAVASCRIPT, unpatched=True) \ No newline at end of file +POLYGLOT_JS_UNPATCHED_SUITE = PolyglotSuite(PolyglotSuiteLanguage.JAVASCRIPT, unpatched=True) diff --git a/evaluator/problem_suites/problem_suite.py b/evaluator/problem_suites/problem_suite.py index a70b5dedf..59253e4ea 100644 --- a/evaluator/problem_suites/problem_suite.py +++ b/evaluator/problem_suites/problem_suite.py @@ -1,20 +1,19 @@ """Base class for problem suites.""" import os -import requests import traceback -import utils.logger as logger - -from enum import Enum -from uuid import UUID -from models.problem import Problem from abc import ABC, abstractmethod +from enum import Enum from typing import Any, List, Tuple -from models.problem import ProblemTestResult +from uuid import UUID + +import requests + +import utils.logger as logger from evaluator.models import EvaluationRunException -from models.evaluation_run import EvaluationRunErrorCode from evaluator.sandbox.sandbox_manager import Sandbox, SandboxManager - +from models.evaluation_run import EvaluationRunErrorCode +from models.problem import Problem, ProblemTestResult class ProblemSuiteName(str, Enum): @@ -23,36 +22,23 @@ class ProblemSuiteName(str, Enum): polyglot_js = "polyglot_js" - class ProblemSuite(ABC): - def _add_problem(self, problem: Problem) -> None: + def _add_problem(self, problem: Problem) -> None: if problem.name in self.problems: logger.fatal(f"Problem {problem.name} already exists") - - self.problems[problem.name] = problem - + self.problems[problem.name] = problem def has_problem_name(self, problem_name: str) -> bool: return problem_name in self.problems - def get_problem(self, problem_name: str) -> Problem: + def get_problem(self, problem_name: str) -> Problem: return self.problems.get(problem_name) - - @abstractmethod - def copy_problem_files_to_directory( - self, - problem: Problem, - dir: str, - *, - include_tests: bool = False - ) -> None: + def copy_problem_files_to_directory(self, problem: Problem, dir: str, *, include_tests: bool = False) -> None: pass - - def initialize_agent_sandbox( self, sandbox_manager: SandboxManager, @@ -62,14 +48,15 @@ def initialize_agent_sandbox( timeout_seconds: int, *, include_solutions: bool = False, - include_tests: bool = False + include_tests: bool = False, ) -> Sandbox: try: + def _on_mount(temp_dir: str): # Create /sandbox/agent.py with open(os.path.join(temp_dir, "agent.py"), "w") as f: f.write(agent_code) - + # Create /sandbox/repo directory sandbox_repo_dir = os.path.join(temp_dir, "repo") os.mkdir(sandbox_repo_dir) @@ -82,34 +69,21 @@ def _on_mount(temp_dir: str): with open(os.path.join(temp_dir, "solution.diff"), "w") as f: f.write(problem.solution_diff) - - return sandbox_manager.initialize_sandbox( name=f"agent-sandbox-{problem.name}-{evaluation_run_id}", script_path=os.path.join(os.path.dirname(__file__), "AGENT_RUNNER.py"), - input_data={ - "problem_statement": problem.problem_statement - }, - env_vars={ - "EVALUATION_RUN_ID": evaluation_run_id, - "AGENT_TIMEOUT": str(timeout_seconds) - }, + input_data={"problem_statement": problem.problem_statement}, + env_vars={"EVALUATION_RUN_ID": evaluation_run_id, "AGENT_TIMEOUT": str(timeout_seconds)}, on_mount=_on_mount, - timeout_seconds=timeout_seconds + timeout_seconds=timeout_seconds, ) except Exception as e: raise EvaluationRunException( EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_AGENT, - f"{EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_AGENT.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" + f"{EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_AGENT.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", ) - - - def run_agent_sandbox( - self, - sandbox_manager: SandboxManager, - agent_sandbox: Sandbox - ) -> Tuple[str, str]: + def run_agent_sandbox(self, sandbox_manager: SandboxManager, agent_sandbox: Sandbox) -> Tuple[str, str]: try: try: sandbox_result_with_logs = sandbox_manager.run_sandbox(agent_sandbox) @@ -122,15 +96,15 @@ def run_agent_sandbox( if timed_out: raise EvaluationRunException( EvaluationRunErrorCode.AGENT_TIMEOUT_RUNNING_AGENT, - f"{EvaluationRunErrorCode.AGENT_TIMEOUT_RUNNING_AGENT.get_error_message()}: The agent exceeded the timeout of {agent_sandbox.timeout_seconds} seconds." + f"{EvaluationRunErrorCode.AGENT_TIMEOUT_RUNNING_AGENT.get_error_message()}: The agent exceeded the timeout of {agent_sandbox.timeout_seconds} seconds.", ) if not sandbox_result_with_logs.success: raise EvaluationRunException( EvaluationRunErrorCode.AGENT_EXCEPTION_RUNNING_AGENT, - f"{EvaluationRunErrorCode.AGENT_EXCEPTION_RUNNING_AGENT.get_error_message()}: {sandbox_result_with_logs.error}\n\nTraceback:\n{sandbox_result_with_logs.traceback}" + f"{EvaluationRunErrorCode.AGENT_EXCEPTION_RUNNING_AGENT.get_error_message()}: {sandbox_result_with_logs.error}\n\nTraceback:\n{sandbox_result_with_logs.traceback}", ) - + return sandbox_result_with_logs.output, sandbox_result_with_logs.logs except EvaluationRunException: @@ -139,27 +113,17 @@ def run_agent_sandbox( except Exception as e: raise EvaluationRunException( EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_AGENT, - f"{EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_AGENT.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" + f"{EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_AGENT.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", ) - - @abstractmethod def initialize_eval_sandbox( - self, - sandbox_manager: SandboxManager, - problem: Problem, - evaluation_run_id: UUID, - patch: str + self, sandbox_manager: SandboxManager, problem: Problem, evaluation_run_id: UUID, patch: str ) -> Any: pass - - @abstractmethod def run_eval_sandbox( - self, - sandbox_manager: SandboxManager, - eval_sandbox: Any + self, sandbox_manager: SandboxManager, eval_sandbox: Any ) -> Tuple[List[ProblemTestResult], str]: - pass \ No newline at end of file + pass diff --git a/evaluator/problem_suites/swebench_verified/swebench_verified_suite.py b/evaluator/problem_suites/swebench_verified/swebench_verified_suite.py index b453a4dfb..601bb227d 100644 --- a/evaluator/problem_suites/swebench_verified/swebench_verified_suite.py +++ b/evaluator/problem_suites/swebench_verified/swebench_verified_suite.py @@ -1,32 +1,44 @@ """The SWE-Bench Verified problem suite.""" -import os import json -import time -import shutil +import os import pathlib +import shutil import subprocess import threading +import time import traceback -import utils.logger as logger - +from typing import Any, Dict, List, Optional, Tuple from uuid import UUID + from pydantic import BaseModel -from utils.docker import get_docker_client -from typing import Any, Dict, List, Tuple, Optional -from utils.diff import validate_diff_for_local_repo -from evaluator.models import EvaluationRunException from swebench.harness.constants import SWEbenchInstance -from utils.temp import create_temp_dir, delete_temp_dir -from models.evaluation_run import EvaluationRunErrorCode -from swebench.harness.test_spec.test_spec import TestSpec -from evaluator.sandbox.sandbox_manager import SandboxManager -from swebench.harness.run_evaluation import make_test_spec, run_instance from swebench.harness.docker_build import build_env_images, build_instance_images -from evaluator.problem_suites.problem_suite import ProblemSuite, ProblemSuiteName -from utils.git import clone_repo, clone_local_repo_at_commit, reset_local_repo_to_commit, verify_commit_exists_in_local_repo -from models.problem import Problem, ProblemTest, ProblemDifficulty, ProblemTestResult, ProblemTestCategory, ProblemTestResultStatus +from swebench.harness.run_evaluation import make_test_spec, run_instance +from swebench.harness.test_spec.test_spec import TestSpec +import utils.logger as logger +from evaluator.models import EvaluationRunException +from evaluator.problem_suites.problem_suite import ProblemSuite, ProblemSuiteName +from evaluator.sandbox.sandbox_manager import SandboxManager +from models.evaluation_run import EvaluationRunErrorCode +from models.problem import ( + Problem, + ProblemDifficulty, + ProblemTest, + ProblemTestCategory, + ProblemTestResult, + ProblemTestResultStatus, +) +from utils.diff import validate_diff_for_local_repo +from utils.docker import get_docker_client +from utils.git import ( + clone_local_repo_at_commit, + clone_repo, + reset_local_repo_to_commit, + verify_commit_exists_in_local_repo, +) +from utils.temp import create_temp_dir, delete_temp_dir def _swebench_verified_difficulty_to_problem_difficulty(difficulty: str) -> Optional[ProblemDifficulty]: @@ -42,13 +54,11 @@ def _swebench_verified_difficulty_to_problem_difficulty(difficulty: str) -> Opti return None - # /evaluator/datasets/swebench_verified SWEBENCH_VERIFIED_DATASET_PATH = str(pathlib.Path(__file__).parent.parent.parent / "datasets" / "swebench_verified") SWEBENCH_MIRROR_REPAIR_LOCK = threading.Lock() - class SWEBenchVerifiedEvaluationSandbox(BaseModel): evaluation_run_id: UUID test_spec: TestSpec @@ -56,60 +66,59 @@ class SWEBenchVerifiedEvaluationSandbox(BaseModel): timeout_seconds: int - class SWEBenchVerifiedSuite(ProblemSuite): def __init__(self): self.problems = {} self.name = ProblemSuiteName.swebench_verified - + logger.info(f"Loading problems from {SWEBENCH_VERIFIED_DATASET_PATH}...") - + # Make sure the swebench_verified.json file exists json_path = os.path.join(SWEBENCH_VERIFIED_DATASET_PATH, "swebench_verified.json") if not os.path.exists(json_path): logger.fatal(f"swebench_verified.json not found at: {json_path}") - + # Open the swebench_verified.json file with open(json_path, "r") as f: problems_list = json.load(f) - + logger.debug(f"Loaded {len(problems_list)} problems from {json_path}") - + # Count unique repositories unique_repos = set() for problem in problems_list: unique_repos.add(problem.get("repo")) - + logger.debug(f"Finding {len(unique_repos)} unique repositories...") - + # Check that all repositories exist in the repos/ directory repos_dir = os.path.join(SWEBENCH_VERIFIED_DATASET_PATH, "repos") if not os.path.exists(repos_dir): os.makedirs(repos_dir, exist_ok=True) - + for repo in unique_repos: # Convert repository format from "owner/name" to directory name format "owner_name" repo_dir_name = repo.replace("/", "_") repo_path = os.path.join(repos_dir, repo_dir_name) - + if not os.path.exists(repo_path): repo_url = f"https://github.com/{repo}.git" clone_repo(repo_url, repo_path) - + logger.debug(f"Found {len(unique_repos)} unique repositories") - + # Process each problem - num_skipped_problems = 0 + # num_skipped_problems = 0 for problem in problems_list: problem_name = problem.get("instance_id") - + # repo = problem.get("repo") # base_commit = problem.get("base_commit") - + # # Verify commit exists in repository # repo_dir_name = repo.replace("/", "_") # repo_path = os.path.join(repos_dir, repo_dir_name) - + # if not verify_commit_exists_in_local_repo(repo_path, base_commit): # logger.fatal(f"Problem {problem_name}: commit {base_commit} not found in repository {repo}") @@ -127,30 +136,28 @@ def __init__(self): for test_name in json.loads(problem.get("FAIL_TO_PASS")): tests.append(ProblemTest(name=test_name, category=ProblemTestCategory.fail_to_pass)) - self._add_problem(Problem( - name=problem_name, - difficulty=_swebench_verified_difficulty_to_problem_difficulty(problem.get("difficulty")), - - problem_statement=problem.get("problem_statement"), - solution_diff=problem.get("patch"), - - # We will store the entire SWE-Bench problem object in the userdata (this is basically a Dict[str, Any]) - # This is so that we can access metadata like the commit hash later on - userdata=problem - )) + self._add_problem( + Problem( + name=problem_name, + difficulty=_swebench_verified_difficulty_to_problem_difficulty(problem.get("difficulty")), + problem_statement=problem.get("problem_statement"), + solution_diff=problem.get("patch"), + # We will store the entire SWE-Bench problem object in the userdata (this is basically a Dict[str, Any]) + # This is so that we can access metadata like the commit hash later on + userdata=problem, + ) + ) # logger.debug(f"Problem {problem_name} verified successfully") - - logger.info(f"Successfully loaded {len(self.problems)} problems from {SWEBENCH_VERIFIED_DATASET_PATH}") - + logger.info(f"Successfully loaded {len(self.problems)} problems from {SWEBENCH_VERIFIED_DATASET_PATH}") def copy_problem_files_to_directory( self, problem: Problem, dir: str, *, - include_tests: bool = False # TODO ADAM + include_tests: bool = False, # TODO ADAM ) -> None: # Get the SWE-Bench problem object swebench_instance = problem.userdata @@ -172,21 +179,20 @@ def copy_problem_files_to_directory( logger.warning(f"Repairing SWE-Bench mirror for {repo} at {local_repo_dir}") shutil.rmtree(local_repo_dir, ignore_errors=True) clone_repo(f"https://github.com/{repo}.git", local_repo_dir) - + # Clone the appropriate repository at the specific commit that the problem requires clone_local_repo_at_commit(local_repo_dir, commit_hash, dir) # Remove future commits from repo reset_local_repo_to_commit(local_repo_dir, commit_hash, dir) - def initialize_eval_sandbox( self, sandbox_manager: SandboxManager, problem: Problem, evaluation_run_id: UUID, patch: str, - timeout_seconds: int + timeout_seconds: int, ) -> SWEBenchVerifiedEvaluationSandbox: try: # Create temporary directory @@ -200,38 +206,32 @@ def initialize_eval_sandbox( if not is_valid: raise EvaluationRunException( EvaluationRunErrorCode.AGENT_INVALID_PATCH, - f"{EvaluationRunErrorCode.AGENT_INVALID_PATCH.get_error_message()}: {error_message}" + f"{EvaluationRunErrorCode.AGENT_INVALID_PATCH.get_error_message()}: {error_message}", ) - - swebench_instance = problem.userdata test_spec = make_test_spec(SWEbenchInstance(**swebench_instance)) - pred = { - "model_name_or_path": str(evaluation_run_id), - "model_patch": patch, - "instance_id": problem.name - } + pred = {"model_name_or_path": str(evaluation_run_id), "model_patch": patch, "instance_id": problem.name} + + return SWEBenchVerifiedEvaluationSandbox( + evaluation_run_id=evaluation_run_id, test_spec=test_spec, pred=pred, timeout_seconds=timeout_seconds + ) - return SWEBenchVerifiedEvaluationSandbox(evaluation_run_id=evaluation_run_id, test_spec=test_spec, pred=pred, timeout_seconds=timeout_seconds) - except EvaluationRunException: raise - + except Exception as e: raise EvaluationRunException( EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_EVAL, - f"{EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" + f"{EvaluationRunErrorCode.VALIDATOR_FAILED_INIT_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", ) - + finally: # Delete temporary directory delete_temp_dir(temp_dir) - - def run_eval_sandbox( self, sandbox_manager: SandboxManager, @@ -245,25 +245,41 @@ def run_eval_sandbox( force_rebuild=False, client=get_docker_client(), run_id=str(eval_sandbox.evaluation_run_id), - timeout=eval_sandbox.timeout_seconds + timeout=eval_sandbox.timeout_seconds, ) # NOTE ADAM: timeout test_results = [] - + tests_status = report[instance_id]["tests_status"] - + for test_name in tests_status["FAIL_TO_PASS"]["success"]: - test_results.append(ProblemTestResult(name=test_name, category=ProblemTestCategory.fail_to_pass, status=ProblemTestResultStatus.PASS)) + test_results.append( + ProblemTestResult( + name=test_name, category=ProblemTestCategory.fail_to_pass, status=ProblemTestResultStatus.PASS + ) + ) for test_name in tests_status["FAIL_TO_PASS"]["failure"]: - test_results.append(ProblemTestResult(name=test_name, category=ProblemTestCategory.fail_to_pass, status=ProblemTestResultStatus.FAIL)) - + test_results.append( + ProblemTestResult( + name=test_name, category=ProblemTestCategory.fail_to_pass, status=ProblemTestResultStatus.FAIL + ) + ) + for test_name in tests_status["PASS_TO_PASS"]["success"]: - test_results.append(ProblemTestResult(name=test_name, category=ProblemTestCategory.pass_to_pass, status=ProblemTestResultStatus.PASS)) + test_results.append( + ProblemTestResult( + name=test_name, category=ProblemTestCategory.pass_to_pass, status=ProblemTestResultStatus.PASS + ) + ) for test_name in tests_status["PASS_TO_PASS"]["failure"]: - test_results.append(ProblemTestResult(name=test_name, category=ProblemTestCategory.pass_to_pass, status=ProblemTestResultStatus.FAIL)) - + test_results.append( + ProblemTestResult( + name=test_name, category=ProblemTestCategory.pass_to_pass, status=ProblemTestResultStatus.FAIL + ) + ) + # NOTE ADAM: /logs/run_evaluation/run_id/run_id/{run_instance.log,test_output.txt} eval_logs = "No evaluation logs available" @@ -272,10 +288,8 @@ def run_eval_sandbox( except Exception as e: raise EvaluationRunException( EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_EVAL, - f"{EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" + f"{EvaluationRunErrorCode.VALIDATOR_FAILED_RUNNING_EVAL.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", ) - - def prebuild_problem_images(self, problem_names: List[str]): MAX_WORKERS = 4 @@ -285,10 +299,10 @@ def prebuild_problem_images(self, problem_names: List[str]): if len(problem_names) == 0: return - logger.info(f"Prebuilding problem images:") + logger.info("Prebuilding problem images:") for problem_name in problem_names: logger.info(f" {problem_name}") - + test_specs = [] for problem_name in problem_names: @@ -301,31 +315,34 @@ def prebuild_problem_images(self, problem_names: List[str]): logger.debug(f"Prebuilding environment images for {len(test_specs)} problems") start_time = time.time() build_successful, build_failed = build_env_images( - client=get_docker_client(), - dataset=test_specs, - force_rebuild=False, - max_workers=MAX_WORKERS + client=get_docker_client(), dataset=test_specs, force_rebuild=False, max_workers=MAX_WORKERS ) elapsed_time = time.time() - start_time if len(build_failed) > 0: - logger.warning(f"Failed to prebuild environment images for {len(build_failed)} of {len(test_specs)} problems") - raise RuntimeError(f"Failed to prebuild environment images for {len(build_failed)} of {len(test_specs)} problems") - logger.debug(f"Successfully prebuilt environment images for {len(test_specs)} problems in {elapsed_time:.1f} seconds") + logger.warning( + f"Failed to prebuild environment images for {len(build_failed)} of {len(test_specs)} problems" + ) + raise RuntimeError( + f"Failed to prebuild environment images for {len(build_failed)} of {len(test_specs)} problems" + ) + logger.debug( + f"Successfully prebuilt environment images for {len(test_specs)} problems in {elapsed_time:.1f} seconds" + ) logger.debug(f"Prebuilding instance images for {len(test_specs)} problems") start_time = time.time() build_successful, build_failed = build_instance_images( - client=get_docker_client(), - dataset=test_specs, - force_rebuild=False, - max_workers=MAX_WORKERS + client=get_docker_client(), dataset=test_specs, force_rebuild=False, max_workers=MAX_WORKERS ) elapsed_time = time.time() - start_time if len(build_failed) > 0: logger.warning(f"Failed to prebuild instance images for {len(build_failed)} of {len(test_specs)} problems") - raise RuntimeError(f"Failed to prebuild instance images for {len(build_failed)} of {len(test_specs)} problems") - logger.debug(f"Successfully prebuilt instance images for {len(test_specs)} problems in {elapsed_time:.1f} seconds") - + raise RuntimeError( + f"Failed to prebuild instance images for {len(build_failed)} of {len(test_specs)} problems" + ) + logger.debug( + f"Successfully prebuilt instance images for {len(test_specs)} problems in {elapsed_time:.1f} seconds" + ) SWEBENCH_VERIFIED_SUITE = SWEBenchVerifiedSuite() diff --git a/evaluator/sandbox/sandbox_manager.py b/evaluator/sandbox/sandbox_manager.py index cbc5ee639..564ce1108 100644 --- a/evaluator/sandbox/sandbox_manager.py +++ b/evaluator/sandbox/sandbox_manager.py @@ -1,15 +1,21 @@ -import os import json -import httpx +import os import shutil -import utils.logger as logger - -from typing import Any, Dict, Callable -from utils.temp import create_temp_dir, delete_temp_dir -from evaluator.models import Sandbox, SandboxResultWithLogs -from utils.docker import DOCKER_PREFIX, get_docker_client, build_docker_image, create_internal_docker_network, connect_docker_container_to_internet, stop_and_delete_all_docker_containers +from typing import Any, Callable, Dict +import httpx +import utils.logger as logger +from evaluator.models import Sandbox, SandboxResultWithLogs +from utils.docker import ( + DOCKER_PREFIX, + build_docker_image, + connect_docker_container_to_internet, + create_internal_docker_network, + get_docker_client, + stop_and_delete_all_docker_containers, +) +from utils.temp import create_temp_dir, delete_temp_dir SANDBOX_NETWORK_NAME = f"{DOCKER_PREFIX}-sandbox-network" @@ -17,7 +23,6 @@ SANDBOX_PROXY_PORT = 80 - class SandboxManager: def __init__(self, inference_gateway_url: str): # Setup inference gateway @@ -40,8 +45,6 @@ def __init__(self, inference_gateway_url: str): build_docker_image(os.path.dirname(__file__) + "/proxy", "sandbox-proxy-image") self._create_sandbox_proxy(inference_gateway_url) - - def _check_inference_gateway(self, inference_gateway_url): logger.info(f"Checking inference gateway URL: {inference_gateway_url}") @@ -52,44 +55,37 @@ def _check_inference_gateway(self, inference_gateway_url): # TODO ADAM: Send inference & embedding requests valid = True - except Exception as e: + except Exception: pass if not valid: logger.fatal(f"Inference gateway URL {inference_gateway_url} is invalid") - - logger.info(f"Inference gateway URL {inference_gateway_url} is valid") - + logger.info(f"Inference gateway URL {inference_gateway_url} is valid") def _create_sandbox_proxy(self, gateway_url): """ Create the sandbox proxy server. - + This is a special sandbox that runs a proxy server (nginx). This is the only sandbox that can access the internet. - + The other sandboxes cannot directly access the internet. So to do inference, they send requests to this proxy server, which forwards appropriate requests to the inference gateway. """ - + logger.info("Running sandbox proxy") self.proxy_container = get_docker_client().containers.run( name=SANDBOX_PROXY_HOST, image=f"{DOCKER_PREFIX}-sandbox-proxy-image", network=SANDBOX_NETWORK_NAME, - environment={ - "GATEWAY_URL": gateway_url, - "GATEWAY_HOST": gateway_url.split("://")[1].split(":")[0] - }, - detach=True + environment={"GATEWAY_URL": gateway_url, "GATEWAY_HOST": gateway_url.split("://")[1].split(":")[0]}, + detach=True, ) connect_docker_container_to_internet(self.proxy_container) - - def initialize_sandbox( self, *, @@ -98,14 +94,14 @@ def initialize_sandbox( input_data: Any = None, env_vars: Dict[str, str] = {}, on_mount: Callable[[str], None] = None, - timeout_seconds: int = None + timeout_seconds: int = None, ) -> Sandbox: name = f"{DOCKER_PREFIX}-{name}" - + # Create temporary directory temp_dir = create_temp_dir() logger.debug(f"Created temporary directory for sandbox <{name}>: {temp_dir}") - + if on_mount is not None: # Call on_mount logger.debug(f"Calling on_mount() for sandbox <{name}>...") @@ -122,7 +118,7 @@ def initialize_sandbox( temp_script_path = os.path.join(temp_dir, script_name) shutil.copy2(script_path, temp_script_path) logger.debug(f"Copied script for sandbox <{name}>: {script_name} --> {temp_script_path}") - + # Create input.json temp_input_json_path = os.path.join(temp_dir, "input.json") with open(temp_input_json_path, "w") as f: @@ -143,28 +139,18 @@ def initialize_sandbox( network=SANDBOX_NETWORK_NAME, environment={ "PYTHONUNBUFFERED": "1", - "PYTHONDONTWRITEBYTECODE": "1", # No __pycache__ + "PYTHONDONTWRITEBYTECODE": "1", # No __pycache__ "SANDBOX_PROXY_URL": f"http://{SANDBOX_PROXY_HOST}:{SANDBOX_PROXY_PORT}", - **env_vars + **env_vars, }, command=command, - detach=True - ) - - return Sandbox( - name=name, - temp_dir=temp_dir, - container=container, - timeout_seconds=timeout_seconds + detach=True, ) + return Sandbox(name=name, temp_dir=temp_dir, container=container, timeout_seconds=timeout_seconds) + def run_sandbox(self, sandbox: Sandbox) -> SandboxResultWithLogs: - def run_sandbox( - self, - sandbox: Sandbox - ) -> SandboxResultWithLogs: - try: sandbox.container.wait(timeout=sandbox.timeout_seconds) @@ -185,4 +171,4 @@ def run_sandbox( sandbox.container.remove() # Remove temporary directory - delete_temp_dir(sandbox.temp_dir) \ No newline at end of file + delete_temp_dir(sandbox.temp_dir) diff --git a/inference_gateway/config.py b/inference_gateway/config.py index 77ac3b658..8cbc383ad 100644 --- a/inference_gateway/config.py +++ b/inference_gateway/config.py @@ -1,14 +1,12 @@ import os -import utils.logger as logger from dotenv import load_dotenv - +import utils.logger as logger load_dotenv() - HOST = os.getenv("HOST") if not HOST: logger.fatal("HOST is not set in .env") @@ -19,7 +17,6 @@ PORT = int(PORT) - USE_DATABASE = os.getenv("USE_DATABASE") if not USE_DATABASE: logger.fatal("USE_DATABASE is not set in .env") @@ -29,20 +26,20 @@ DATABASE_USERNAME = os.getenv("DATABASE_USERNAME") if not DATABASE_USERNAME: logger.fatal("DATABASE_USERNAME is not set in .env") - + DATABASE_PASSWORD = os.getenv("DATABASE_PASSWORD") if not DATABASE_PASSWORD: logger.fatal("DATABASE_PASSWORD is not set in .env") - + DATABASE_HOST = os.getenv("DATABASE_HOST") if not DATABASE_HOST: logger.fatal("DATABASE_HOST is not set in .env") - + DATABASE_PORT = os.getenv("DATABASE_PORT") if not DATABASE_PORT: logger.fatal("DATABASE_PORT is not set in .env") DATABASE_PORT = int(DATABASE_PORT) - + DATABASE_NAME = os.getenv("DATABASE_NAME") if not DATABASE_NAME: logger.fatal("DATABASE_NAME is not set in .env") @@ -53,14 +50,12 @@ CHECK_EVALUATION_RUNS = CHECK_EVALUATION_RUNS.lower() == "true" - MAX_COST_PER_EVALUATION_RUN_USD = os.getenv("MAX_COST_PER_EVALUATION_RUN_USD") if not MAX_COST_PER_EVALUATION_RUN_USD: logger.fatal("MAX_COST_PER_EVALUATION_RUN_USD is not set in .env") MAX_COST_PER_EVALUATION_RUN_USD = float(MAX_COST_PER_EVALUATION_RUN_USD) - USE_CHUTES = os.getenv("USE_CHUTES") if not USE_CHUTES: logger.fatal("USE_CHUTES is not set in .env") @@ -85,7 +80,6 @@ CHUTES_WEIGHT = int(CHUTES_WEIGHT) - USE_TARGON = os.getenv("USE_TARGON") if not USE_TARGON: logger.fatal("USE_TARGON is not set in .env") @@ -125,7 +119,6 @@ OPENROUTER_WEIGHT = int(OPENROUTER_WEIGHT) - if not USE_CHUTES and not USE_TARGON and not USE_OPENROUTER: logger.fatal("Either USE_CHUTES or USE_TARGON or USE_OPENROUTER must be set to True in .env") @@ -141,7 +134,6 @@ TEST_EMBEDDING_MODELS = TEST_EMBEDDING_MODELS.lower() == "true" - logger.info("=== Inference Gateway Configuration ===") logger.info(f"Host: {HOST}") @@ -182,4 +174,4 @@ else: logger.warning("Not Using OpenRouter!") -logger.info("=======================================") \ No newline at end of file +logger.info("=======================================") diff --git a/inference_gateway/cost_hash_map.py b/inference_gateway/cost_hash_map.py index 8c973de1b..a7ce61afb 100644 --- a/inference_gateway/cost_hash_map.py +++ b/inference_gateway/cost_hash_map.py @@ -6,33 +6,33 @@ # information is tracked here in memory. import time - from uuid import UUID -from pydantic import BaseModel +from pydantic import BaseModel +COST_HASH_MAP_CLEANUP_INTERVAL_SECONDS = 60 # 1 minute -COST_HASH_MAP_CLEANUP_INTERVAL_SECONDS = 60 # 1 minute class CostHashMapEntry(BaseModel): cost: float last_accessed_at: float + class CostHashMap: def __init__(self): self.cost_hash_map = {} self.last_cleanup_at = time.time() - - def _cleanup(self): now = time.time() if now - self.last_cleanup_at > COST_HASH_MAP_CLEANUP_INTERVAL_SECONDS: - self.cost_hash_map = {k: v for k, v in self.cost_hash_map.items() if now - v.last_accessed_at < COST_HASH_MAP_CLEANUP_INTERVAL_SECONDS} + self.cost_hash_map = { + k: v + for k, v in self.cost_hash_map.items() + if now - v.last_accessed_at < COST_HASH_MAP_CLEANUP_INTERVAL_SECONDS + } self.last_cleanup_at = now - - def get_cost(self, uuid: UUID) -> float: self._cleanup() @@ -51,4 +51,4 @@ def add_cost(self, uuid: UUID, cost: float): entry.cost += cost entry.last_accessed_at = time.time() else: - self.cost_hash_map[uuid] = CostHashMapEntry(cost=cost, last_accessed_at=time.time()) \ No newline at end of file + self.cost_hash_map[uuid] = CostHashMapEntry(cost=cost, last_accessed_at=time.time()) diff --git a/inference_gateway/main.py b/inference_gateway/main.py index fcb97aa0e..4eb7ea722 100644 --- a/inference_gateway/main.py +++ b/inference_gateway/main.py @@ -1,26 +1,34 @@ import random -import uvicorn -from inference_gateway.providers.openrouter import OpenRouterProvider -import utils.logger as logger -import inference_gateway.config as config - -from uuid import UUID -from typing import List +from contextlib import asynccontextmanager from functools import wraps -from pydantic import BaseModel +from typing import List +from uuid import UUID + +import uvicorn from fastapi import FastAPI, HTTPException -from contextlib import asynccontextmanager -from models.evaluation_run import EvaluationRunStatus +from pydantic import BaseModel + +import inference_gateway.config as config +import utils.logger as logger from inference_gateway.cost_hash_map import CostHashMap -from inference_gateway.providers.provider import Provider +from inference_gateway.models import ( + EmbeddingModelInfo, + EmbeddingRequest, + EmbeddingResponse, + InferenceModelInfo, + InferenceRequest, + InferenceResponse, + InferenceToolMode, +) from inference_gateway.providers.chutes import ChutesProvider +from inference_gateway.providers.openrouter import OpenRouterProvider +from inference_gateway.providers.provider import Provider from inference_gateway.providers.targon import TargonProvider -from queries.evaluation_run import get_evaluation_run_status_by_id +from models.evaluation_run import EvaluationRunStatus from queries.embedding import create_new_embedding, update_embedding_by_id +from queries.evaluation_run import get_evaluation_run_status_by_id from queries.inference import create_new_inference, update_inference_by_id -from utils.database import initialize_database, get_debug_query_info, deinitialize_database -from inference_gateway.models import EmbeddingRequest, InferenceRequest, EmbeddingResponse, InferenceResponse, InferenceToolMode, EmbeddingModelInfo, InferenceModelInfo - +from utils.database import deinitialize_database, get_debug_query_info, initialize_database class WeightedProvider: @@ -28,17 +36,18 @@ def __init__(self, provider: Provider, weight: int): self.provider = provider self.weight = weight -providers = [] +providers = [] def get_provider_that_supports_model_for_inference(model_name: str) -> Provider: - inference_providers = [wp for wp in providers if wp.provider.is_model_supported_for_inference(model_name)] + inference_providers = [wp for wp in providers if wp.provider.is_model_supported_for_inference(model_name)] if not inference_providers: return None chosen = random.choices(inference_providers, weights=[wp.weight for wp in inference_providers], k=1)[0] return chosen.provider + def get_provider_that_supports_model_for_embedding(model_name: str) -> Provider: embedding_providers = [wp for wp in providers if wp.provider.is_model_supported_for_embedding(model_name)] if not embedding_providers: @@ -47,7 +56,6 @@ def get_provider_that_supports_model_for_embedding(model_name: str) -> Provider: return chosen.provider - @asynccontextmanager async def lifespan(app: FastAPI): if config.USE_DATABASE: @@ -56,11 +64,9 @@ async def lifespan(app: FastAPI): password=config.DATABASE_PASSWORD, host=config.DATABASE_HOST, port=config.DATABASE_PORT, - name=config.DATABASE_NAME + name=config.DATABASE_NAME, ) - - global providers if config.USE_CHUTES: providers.append(WeightedProvider(await ChutesProvider().init(), weight=config.CHUTES_WEIGHT)) @@ -75,23 +81,13 @@ async def lifespan(app: FastAPI): if config.TEST_EMBEDDING_MODELS: await wp.provider.test_all_embedding_models() - - yield - - if config.USE_DATABASE: await deinitialize_database() - -app = FastAPI( - title="Inference Gateway", - description="Inference gateway server", - lifespan=lifespan -) - +app = FastAPI(title="Inference Gateway", description="Inference gateway server", lifespan=lifespan) def handle_http_exceptions(func): @@ -102,14 +98,13 @@ async def wrapper(*args, **kwargs): except HTTPException as e: logger.error(f"HTTP exception: {e.status_code} {e.detail}") raise - return wrapper + return wrapper cost_hash_map = CostHashMap() - # NOTE ADAM: inference@main.py -> Handles HTTP exceptions and database # inference@providers/provider.py -> Handles logging # inference@providers/*.py -> Handles inference @@ -119,35 +114,31 @@ async def inference(request: InferenceRequest) -> InferenceResponse: # If you specify a tool mode of NONE, you must not specify any tools if request.tool_mode == InferenceToolMode.NONE and request.tools: raise HTTPException( - status_code=422, - detail="If you specify a tool mode of NONE, you must not specify any tools." + status_code=422, detail="If you specify a tool mode of NONE, you must not specify any tools." ) # If you specify a tool mode of REQUIRED, you must specify at least one tool if request.tool_mode == InferenceToolMode.REQUIRED and not request.tools: raise HTTPException( - status_code=422, - detail="If you specify a tool mode of REQUIRED, you must specify at least one tool." + status_code=422, detail="If you specify a tool mode of REQUIRED, you must specify at least one tool." ) - - if config.USE_DATABASE and config.CHECK_EVALUATION_RUNS: # Get the status of the evaluation run evaluation_run_status = await get_evaluation_run_status_by_id(request.evaluation_run_id) - + # Make sure the evaluation run actually exists if evaluation_run_status is None: raise HTTPException( status_code=400, - detail=f"No evaluation run exists with the given evaluation run ID {request.evaluation_run_id}." + detail=f"No evaluation run exists with the given evaluation run ID {request.evaluation_run_id}.", ) - + # Make sure the evaluation run is in the running_agent state if evaluation_run_status != EvaluationRunStatus.running_agent: raise HTTPException( status_code=400, - detail=f"The evaluation run with ID {request.evaluation_run_id} is not in the running_agent state (current state: {evaluation_run_status.value})." + detail=f"The evaluation run with ID {request.evaluation_run_id} is not in the running_agent state (current state: {evaluation_run_status.value}).", ) # Make sure the evaluation run has not reached or exceeded its cost limit @@ -155,27 +146,23 @@ async def inference(request: InferenceRequest) -> InferenceResponse: if cost >= config.MAX_COST_PER_EVALUATION_RUN_USD: raise HTTPException( status_code=429, - detail=f"The evaluation run with ID {request.evaluation_run_id} has reached or exceeded the evaluation run cost limit of {config.MAX_COST_PER_EVALUATION_RUN_USD} USD (current cost: {cost} USD)." + detail=f"The evaluation run with ID {request.evaluation_run_id} has reached or exceeded the evaluation run cost limit of {config.MAX_COST_PER_EVALUATION_RUN_USD} USD (current cost: {cost} USD).", ) - - # Make sure we support the model for inference provider = get_provider_that_supports_model_for_inference(request.model) if not provider: raise HTTPException( - status_code=404, - detail=f"The model {request.model} is not supported by Ridges for inference." + status_code=404, detail=f"The model {request.model} is not supported by Ridges for inference." ) if config.USE_DATABASE: inference_id = await create_new_inference( evaluation_run_id=request.evaluation_run_id, - provider=provider.name.lower(), model=request.model, temperature=request.temperature, - messages=request.messages + messages=request.messages, ) response = await provider.inference( @@ -183,34 +170,26 @@ async def inference(request: InferenceRequest) -> InferenceResponse: temperature=request.temperature, messages=request.messages, tool_mode=request.tool_mode, - tools=request.tools + tools=request.tools, ) if config.USE_DATABASE: await update_inference_by_id( inference_id=inference_id, - status_code=response.status_code, response=response.content if response.status_code == 200 else response.error_message, num_input_tokens=response.num_input_tokens, num_output_tokens=response.num_output_tokens, - cost_usd=response.cost_usd + cost_usd=response.cost_usd, ) if response.cost_usd is not None: cost_hash_map.add_cost(request.evaluation_run_id, response.cost_usd) if response.status_code == 200: - return InferenceResponse( - content=response.content, - tool_calls=response.tool_calls - ) + return InferenceResponse(content=response.content, tool_calls=response.tool_calls) else: - raise HTTPException( - status_code=response.status_code, - detail=response.error_message - ) - + raise HTTPException(status_code=response.status_code, detail=response.error_message) # NOTE ADAM: embedding@main.py -> Handles HTTP exceptions and database @@ -222,19 +201,19 @@ async def embedding(request: EmbeddingRequest) -> EmbeddingResponse: if config.USE_DATABASE and config.CHECK_EVALUATION_RUNS: # Get the status of the evaluation run evaluation_run_status = await get_evaluation_run_status_by_id(request.evaluation_run_id) - + # Make sure the evaluation run actually exists if evaluation_run_status is None: raise HTTPException( status_code=400, - detail=f"No evaluation run exists with the given evaluation run ID {request.evaluation_run_id}." + detail=f"No evaluation run exists with the given evaluation run ID {request.evaluation_run_id}.", ) - + # Make sure the evaluation run is in the running_agent state if evaluation_run_status != EvaluationRunStatus.running_agent: raise HTTPException( status_code=400, - detail=f"The evaluation run with ID {request.evaluation_run_id} is not in the running_agent state (current state: {evaluation_run_status.value})." + detail=f"The evaluation run with ID {request.evaluation_run_id} is not in the running_agent state (current state: {evaluation_run_status.value}).", ) # Make sure the evaluation run has not reached or exceeded its cost limit @@ -242,56 +221,42 @@ async def embedding(request: EmbeddingRequest) -> EmbeddingResponse: if cost >= config.MAX_COST_PER_EVALUATION_RUN_USD: raise HTTPException( status_code=429, - detail=f"The evaluation run with ID {request.evaluation_run_id} has reached or exceeded the evaluation run cost limit of {config.MAX_COST_PER_EVALUATION_RUN_USD} USD (current cost: {cost} USD)." + detail=f"The evaluation run with ID {request.evaluation_run_id} has reached or exceeded the evaluation run cost limit of {config.MAX_COST_PER_EVALUATION_RUN_USD} USD (current cost: {cost} USD).", ) - - # Make sure we support the model for embedding provider = get_provider_that_supports_model_for_embedding(request.model) if not provider: raise HTTPException( - status_code=404, - detail=f"The model {request.model} is not supported by Ridges for embedding." + status_code=404, detail=f"The model {request.model} is not supported by Ridges for embedding." ) if config.USE_DATABASE: embedding_id = await create_new_embedding( evaluation_run_id=request.evaluation_run_id, - provider=provider.name.lower(), model=request.model, - input=request.input + input=request.input, ) - response = await provider.embedding( - model_name=request.model, - input=request.input - ) + response = await provider.embedding(model_name=request.model, input=request.input) if config.USE_DATABASE: await update_embedding_by_id( embedding_id=embedding_id, - status_code=response.status_code, response=response.embedding if response.status_code == 200 else response.error_message, num_input_tokens=response.num_input_tokens, - cost_usd=response.cost_usd + cost_usd=response.cost_usd, ) if response.cost_usd is not None: cost_hash_map.add_cost(request.evaluation_run_id, response.cost_usd) - + if response.status_code == 200: - return EmbeddingResponse( - embedding=response.embedding - ) + return EmbeddingResponse(embedding=response.embedding) else: - raise HTTPException( - status_code=response.status_code, - detail=response.error_message - ) - + raise HTTPException(status_code=response.status_code, detail=response.error_message) class UsageResponse(BaseModel): @@ -299,6 +264,7 @@ class UsageResponse(BaseModel): remaining_cost_usd: float max_cost_usd: float + @app.get("/api/usage") @handle_http_exceptions async def usage(evaluation_run_id: UUID) -> UsageResponse: @@ -306,28 +272,26 @@ async def usage(evaluation_run_id: UUID) -> UsageResponse: return UsageResponse( used_cost_usd=used_cost_usd, remaining_cost_usd=config.MAX_COST_PER_EVALUATION_RUN_USD - used_cost_usd, - max_cost_usd=config.MAX_COST_PER_EVALUATION_RUN_USD + max_cost_usd=config.MAX_COST_PER_EVALUATION_RUN_USD, ) - @app.get("/api/inference-models") @handle_http_exceptions async def inference_models() -> List[InferenceModelInfo]: return [model for wp in providers for model in wp.provider.inference_models] + @app.get("/api/embedding-models") @handle_http_exceptions async def embedding_models() -> List[EmbeddingModelInfo]: return [model for wp in providers for model in wp.provider.embedding_models] - @app.get("/debug/query-info") async def debug_query_info(): return get_debug_query_info() - if __name__ == "__main__": - uvicorn.run(app, host=config.HOST, port=config.PORT) \ No newline at end of file + uvicorn.run(app, host=config.HOST, port=config.PORT) diff --git a/inference_gateway/models.py b/inference_gateway/models.py index 6283df89b..040a72cf6 100644 --- a/inference_gateway/models.py +++ b/inference_gateway/models.py @@ -1,15 +1,14 @@ import json - from enum import Enum -from uuid import UUID -from pydantic import BaseModel from typing import Any, List, Optional +from uuid import UUID + from openai.types.chat import ChatCompletionToolChoiceOptionParam +from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCallUnion +from openai.types.chat.chat_completion_tool_param import ChatCompletionToolParam from openai.types.shared_params.function_definition import FunctionDefinition from openai.types.shared_params.function_parameters import FunctionParameters -from openai.types.chat.chat_completion_tool_param import ChatCompletionToolParam -from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCallUnion - +from pydantic import BaseModel # Model Info @@ -21,12 +20,16 @@ class InferenceModelInfo(BaseModel): cost_usd_per_million_output_tokens: float def get_cost_usd(self, num_input_tokens: int, num_output_tokens: int) -> float: - return (num_input_tokens / 1_000_000) * self.cost_usd_per_million_input_tokens + \ - (num_output_tokens / 1_000_000) * self.cost_usd_per_million_output_tokens + return (num_input_tokens / 1_000_000) * self.cost_usd_per_million_input_tokens + ( + num_output_tokens / 1_000_000 + ) * self.cost_usd_per_million_output_tokens + class EmbeddingModelPricingMode(Enum): PER_TOKEN = "per_token" PER_SECOND = "per_second" + + class EmbeddingModelInfo(BaseModel): name: str external_name: str @@ -38,36 +41,43 @@ class EmbeddingModelInfo(BaseModel): def get_cost_usd(self, num_input_tokens: int, num_seconds: float) -> float: if self.pricing_mode == EmbeddingModelPricingMode.PER_TOKEN: return (num_input_tokens / 1_000_000) * self.cost_usd_per_million_input_tokens - else: # if self.pricing_mode == EmbeddingModelPricingMode.PER_SECOND: + else: # if self.pricing_mode == EmbeddingModelPricingMode.PER_SECOND: return num_seconds * self.cost_usd_per_second - # Results class InferenceToolCallArgument(BaseModel): name: str value: Any + + class InferenceToolCall(BaseModel): name: str arguments: List[InferenceToolCallArgument] -def openai_tool_calls_to_inference_tool_calls(openai_tool_calls: List[ChatCompletionMessageToolCallUnion]) -> List[InferenceToolCall]: + +def openai_tool_calls_to_inference_tool_calls( + openai_tool_calls: List[ChatCompletionMessageToolCallUnion], +) -> List[InferenceToolCall]: inference_tool_calls = [] - + for openai_tool_call in openai_tool_calls: try: arguments_dict = json.loads(openai_tool_call.function.arguments) except json.JSONDecodeError: # TODO ADAM arguments_dict = {} - - inference_tool_calls.append(InferenceToolCall( - name=openai_tool_call.function.name, - arguments=[InferenceToolCallArgument(name=name, value=value) for name, value in arguments_dict.items()] - )) - + + inference_tool_calls.append( + InferenceToolCall( + name=openai_tool_call.function.name, + arguments=[InferenceToolCallArgument(name=name, value=value) for name, value in arguments_dict.items()], + ) + ) + return inference_tool_calls + class InferenceResult(BaseModel): status_code: int @@ -76,29 +86,28 @@ class InferenceResult(BaseModel): tool_calls: Optional[List[InferenceToolCall]] = None # if status_code != 200 error_message: Optional[str] = None - + num_input_tokens: Optional[int] = None num_output_tokens: Optional[int] = None cost_usd: Optional[float] = None + class EmbeddingResult(BaseModel): status_code: int - - embedding: Optional[List[float]] = None # if status_code == 200 - error_message: Optional[str] = None # if status_code != 200 - + + embedding: Optional[List[float]] = None # if status_code == 200 + error_message: Optional[str] = None # if status_code != 200 + num_input_tokens: Optional[int] = None cost_usd: Optional[float] = None - # Inference class InferenceMessage(BaseModel): role: str content: str - class InferenceToolParameterType(Enum): BOOLEAN = "boolean" INTEGER = "integer" @@ -106,49 +115,55 @@ class InferenceToolParameterType(Enum): STRING = "string" ARRAY = "array" OBJECT = "object" - + + class InferenceToolParameter(BaseModel): type: InferenceToolParameterType name: str description: str required: bool = False + def inference_tool_parameters_to_openai_parameters(parameters: List[InferenceToolParameter]) -> FunctionParameters: return { "properties": { - parameter.name: { - "type": parameter.type.value, - "description": parameter.description - } for parameter in parameters + parameter.name: {"type": parameter.type.value, "description": parameter.description} + for parameter in parameters }, - "required": [parameter.name for parameter in parameters if parameter.required] + "required": [parameter.name for parameter in parameters if parameter.required], } + class InferenceTool(BaseModel): name: str description: str parameters: List[InferenceToolParameter] + def inference_tools_to_openai_tools(tools: List[InferenceTool]) -> List[ChatCompletionToolParam]: - return [ChatCompletionToolParam( - type="function", - function=FunctionDefinition( - name=tool.name, - description=tool.description, - parameters=inference_tool_parameters_to_openai_parameters(tool.parameters) + return [ + ChatCompletionToolParam( + type="function", + function=FunctionDefinition( + name=tool.name, + description=tool.description, + parameters=inference_tool_parameters_to_openai_parameters(tool.parameters), + ), ) - ) for tool in tools] + for tool in tools + ] + class InferenceToolMode(Enum): NONE = "none" AUTO = "auto" REQUIRED = "required" + def inference_tool_mode_to_openai_tool_choice(tool_mode: InferenceToolMode) -> ChatCompletionToolChoiceOptionParam: return tool_mode.value - # HTTP class InferenceRequest(BaseModel): evaluation_run_id: UUID @@ -158,16 +173,17 @@ class InferenceRequest(BaseModel): tool_mode: Optional[InferenceToolMode] = InferenceToolMode.NONE tools: Optional[List[InferenceTool]] = None + class InferenceResponse(BaseModel): content: str tool_calls: List[InferenceToolCall] - class EmbeddingRequest(BaseModel): evaluation_run_id: UUID model: str input: str + class EmbeddingResponse(BaseModel): - embedding: List[float] \ No newline at end of file + embedding: List[float] diff --git a/inference_gateway/providers/chutes.py b/inference_gateway/providers/chutes.py index 14e2f7426..a4e884480 100644 --- a/inference_gateway/providers/chutes.py +++ b/inference_gateway/providers/chutes.py @@ -1,20 +1,31 @@ -from collections import defaultdict +from time import time from types import SimpleNamespace -import httpx -import utils.logger as logger -import inference_gateway.config as config +from typing import List, Optional -from time import time +import httpx +from openai import APIStatusError, AsyncOpenAI, AsyncStream from pydantic import BaseModel -from typing import List, Optional -from openai import AsyncOpenAI, APIStatusError, AsyncStream -from inference_gateway.providers.provider import Provider -from inference_gateway.models import InferenceTool, EmbeddingResult, InferenceResult, InferenceMessage, InferenceToolMode, EmbeddingModelInfo, InferenceModelInfo, EmbeddingModelPricingMode, inference_tools_to_openai_tools, inference_tool_mode_to_openai_tool_choice, openai_tool_calls_to_inference_tool_calls +import inference_gateway.config as config +import utils.logger as logger +from inference_gateway.models import ( + EmbeddingModelInfo, + EmbeddingModelPricingMode, + EmbeddingResult, + InferenceMessage, + InferenceModelInfo, + InferenceResult, + InferenceTool, + InferenceToolMode, + inference_tool_mode_to_openai_tool_choice, + inference_tools_to_openai_tools, + openai_tool_calls_to_inference_tool_calls, +) +from inference_gateway.providers.provider import Provider if config.USE_CHUTES: - CHUTES_INFERENCE_MODELS_URL = f"{config.CHUTES_INFERENCE_BASE_URL}/models" # https://llm.chutes.ai/v1/models - CHUTES_EMBEDDING_MODELS_URL = "https://api.chutes.ai/chutes/?template=embedding" # TODO ADAM + CHUTES_INFERENCE_MODELS_URL = f"{config.CHUTES_INFERENCE_BASE_URL}/models" # https://llm.chutes.ai/v1/models + CHUTES_EMBEDDING_MODELS_URL = "https://api.chutes.ai/chutes/?template=embedding" # TODO ADAM class WhitelistedChutesModel(BaseModel): @@ -26,6 +37,7 @@ def __init__(self, **data): if self.chutes_name is None: self.chutes_name = self.name + if config.USE_CHUTES: WHITELISTED_CHUTES_INFERENCE_MODELS = [ WhitelistedChutesModel(name="deepseek-ai/DeepSeek-R1-0528", chutes_name="deepseek-ai/DeepSeek-R1-0528-TEE"), @@ -40,9 +52,7 @@ def __init__(self, **data): WhitelistedChutesModel(name="MiniMaxAI/MiniMax-M2.5", chutes_name="MiniMaxAI/MiniMax-M2.5-TEE"), ] -WHITELISTED_CHUTES_EMBEDDING_MODELS = [ - WhitelistedChutesModel(name="Qwen/Qwen3-Embedding-8B") -] +WHITELISTED_CHUTES_EMBEDDING_MODELS = [WhitelistedChutesModel(name="Qwen/Qwen3-Embedding-8B")] class ChutesProvider(Provider): @@ -61,28 +71,43 @@ async def init(self) -> "ChutesProvider": logger.info(f"Fetched {CHUTES_INFERENCE_MODELS_URL}") # Add whitelisted inference models - for whitelisted_chutes_model in WHITELISTED_CHUTES_INFERENCE_MODELS: - chutes_model = next((chutes_model for chutes_model in chutes_inference_models_response if chutes_model["id"] == whitelisted_chutes_model.chutes_name), None) + for whitelisted_chutes_model in WHITELISTED_CHUTES_INFERENCE_MODELS: + chutes_model = next( + ( + chutes_model + for chutes_model in chutes_inference_models_response + if chutes_model["id"] == whitelisted_chutes_model.chutes_name + ), + None, + ) if not chutes_model: - logger.fatal(f"Whitelisted Chutes inference model {whitelisted_chutes_model.chutes_name} is not supported by Chutes") - - if not "text" in chutes_model["input_modalities"]: - logger.fatal(f"Whitelisted Chutes inference model {whitelisted_chutes_model.chutes_name} does not support text input") - if not "text" in chutes_model["output_modalities"]: - logger.fatal(f"Whitelisted Chutes inference model {whitelisted_chutes_model.chutes_name} does not support text output") + logger.fatal( + f"Whitelisted Chutes inference model {whitelisted_chutes_model.chutes_name} is not supported by Chutes" + ) + + if "text" not in chutes_model["input_modalities"]: + logger.fatal( + f"Whitelisted Chutes inference model {whitelisted_chutes_model.chutes_name} does not support text input" + ) + if "text" not in chutes_model["output_modalities"]: + logger.fatal( + f"Whitelisted Chutes inference model {whitelisted_chutes_model.chutes_name} does not support text output" + ) chutes_model_pricing = chutes_model["pricing"] max_input_tokens = chutes_model["context_length"] cost_usd_per_million_input_tokens = chutes_model_pricing["prompt"] cost_usd_per_million_output_tokens = chutes_model_pricing["completion"] - self.inference_models.append(InferenceModelInfo( - name=whitelisted_chutes_model.name, - external_name=whitelisted_chutes_model.chutes_name, - max_input_tokens=max_input_tokens, - cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, - cost_usd_per_million_output_tokens=cost_usd_per_million_output_tokens - )) + self.inference_models.append( + InferenceModelInfo( + name=whitelisted_chutes_model.name, + external_name=whitelisted_chutes_model.chutes_name, + max_input_tokens=max_input_tokens, + cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, + cost_usd_per_million_output_tokens=cost_usd_per_million_output_tokens, + ) + ) logger.info(f"Found whitelisted Chutes inference model {whitelisted_chutes_model.name}:") logger.info(f" Max input tokens: {max_input_tokens}") @@ -100,34 +125,43 @@ async def init(self) -> "ChutesProvider": logger.info(f"Fetched {CHUTES_EMBEDDING_MODELS_URL}") # Add whitelisted embedding models - for whitelisted_chutes_model in WHITELISTED_CHUTES_EMBEDDING_MODELS: - chutes_model = next((chutes_model for chutes_model in chutes_embedding_models_response if chutes_model["name"] == whitelisted_chutes_model.chutes_name), None) + for whitelisted_chutes_model in WHITELISTED_CHUTES_EMBEDDING_MODELS: + chutes_model = next( + ( + chutes_model + for chutes_model in chutes_embedding_models_response + if chutes_model["name"] == whitelisted_chutes_model.chutes_name + ), + None, + ) if not chutes_model: - logger.fatal(f"Whitelisted Chutes embedding model {whitelisted_chutes_model.chutes_name} is not supported by Chutes") + logger.fatal( + f"Whitelisted Chutes embedding model {whitelisted_chutes_model.chutes_name} is not supported by Chutes" + ) - max_input_tokens = 40960 # TODO ADAM + max_input_tokens = 40960 # TODO ADAM cost_usd_per_second = chutes_model["current_estimated_price"]["usd"]["second"] - self.embedding_models.append(EmbeddingModelInfo( - name=whitelisted_chutes_model.name, - external_name=whitelisted_chutes_model.chutes_name, - max_input_tokens=max_input_tokens, - pricing_mode=EmbeddingModelPricingMode.PER_SECOND, - cost_usd_per_second=cost_usd_per_second - )) + self.embedding_models.append( + EmbeddingModelInfo( + name=whitelisted_chutes_model.name, + external_name=whitelisted_chutes_model.chutes_name, + max_input_tokens=max_input_tokens, + pricing_mode=EmbeddingModelPricingMode.PER_SECOND, + cost_usd_per_second=cost_usd_per_second, + ) + ) logger.info(f"Found whitelisted Chutes inference model {whitelisted_chutes_model.name}:") logger.info(f" Max input tokens: {max_input_tokens}") logger.info(f" Input cost (USD per second): {cost_usd_per_second}") self.chutes_inference_client = AsyncOpenAI( - base_url=config.CHUTES_INFERENCE_BASE_URL, - api_key=config.CHUTES_API_KEY + base_url=config.CHUTES_INFERENCE_BASE_URL, api_key=config.CHUTES_API_KEY ) self.chutes_embedding_client = AsyncOpenAI( - base_url=config.CHUTES_EMBEDDING_BASE_URL, - api_key=config.CHUTES_API_KEY + base_url=config.CHUTES_EMBEDDING_BASE_URL, api_key=config.CHUTES_API_KEY ) return self @@ -139,7 +173,7 @@ async def _inference( temperature: float, messages: List[InferenceMessage], tool_mode: InferenceToolMode, - tools: Optional[List[InferenceTool]] + tools: Optional[List[InferenceTool]], ) -> InferenceResult: try: completion_stream: AsyncStream = await self.chutes_inference_client.chat.completions.create( @@ -149,7 +183,7 @@ async def _inference( tool_choice=inference_tool_mode_to_openai_tool_choice(tool_mode), tools=inference_tools_to_openai_tools(tools) if tools else None, stream=True, - stream_options={"include_usage": True} + stream_options={"include_usage": True}, ) streamed_completion = [] tool_calls = dict() @@ -182,7 +216,8 @@ async def _inference( message_content = "".join(streamed_completion) message_tool_calls = [ - tool_calls[idx] for idx in sorted(tool_calls) # sort by index + tool_calls[idx] + for idx in sorted(tool_calls) # sort by index ] num_input_tokens = last_chunk.usage.prompt_tokens @@ -191,39 +226,27 @@ async def _inference( return InferenceResult( status_code=200, - content=message_content, tool_calls=openai_tool_calls_to_inference_tool_calls(message_tool_calls) if message_tool_calls else [], - num_input_tokens=num_input_tokens, num_output_tokens=num_output_tokens, - cost_usd=cost_usd + cost_usd=cost_usd, ) except APIStatusError as e: # Chutes returned 4xx or 5xx - return InferenceResult( - status_code=e.status_code, - error_message=e.response.text - ) + return InferenceResult(status_code=e.status_code, error_message=e.response.text) except Exception as e: return InferenceResult( - status_code=-1, - error_message=f"Error in ChutesProvider._inference(): {type(e).__name__}: {str(e)}" + status_code=-1, error_message=f"Error in ChutesProvider._inference(): {type(e).__name__}: {str(e)}" ) - async def _embedding( - self, - *, - model_info: EmbeddingModelInfo, - input: str - ) -> EmbeddingResult: + async def _embedding(self, *, model_info: EmbeddingModelInfo, input: str) -> EmbeddingResult: try: start_time = time() create_embedding_response = await self.chutes_embedding_client.embeddings.create( - model=model_info.external_name, - input=input + model=model_info.external_name, input=input ) end_time = time() @@ -233,23 +256,14 @@ async def _embedding( cost_usd = model_info.get_cost_usd(num_input_tokens, end_time - start_time) return EmbeddingResult( - status_code=200, - - embedding=embedding, - - num_input_tokens=num_input_tokens, - cost_usd=cost_usd + status_code=200, embedding=embedding, num_input_tokens=num_input_tokens, cost_usd=cost_usd ) except APIStatusError as e: # Chutes returned 4xx or 5xx - return EmbeddingResult( - status_code=e.status_code, - error_message=e.response.text - ) + return EmbeddingResult(status_code=e.status_code, error_message=e.response.text) except Exception as e: return EmbeddingResult( - status_code=-1, - error_message=f"Error in ChutesProvider._embedding(): {type(e).__name__}: {str(e)}" + status_code=-1, error_message=f"Error in ChutesProvider._embedding(): {type(e).__name__}: {str(e)}" ) diff --git a/inference_gateway/providers/openrouter.py b/inference_gateway/providers/openrouter.py index ada46e395..c055d2ed5 100644 --- a/inference_gateway/providers/openrouter.py +++ b/inference_gateway/providers/openrouter.py @@ -1,20 +1,32 @@ -from collections import defaultdict +from time import time from types import SimpleNamespace -import httpx -import utils.logger as logger -import inference_gateway.config as config +from typing import List, Optional -from time import time +import httpx +from openai import APIStatusError, AsyncOpenAI, AsyncStream from pydantic import BaseModel -from typing import List, Optional -from openai import AsyncOpenAI, APIStatusError, AsyncStream -from inference_gateway.providers.provider import Provider -from inference_gateway.models import InferenceTool, EmbeddingResult, InferenceResult, InferenceMessage, InferenceToolMode, EmbeddingModelInfo, InferenceModelInfo, EmbeddingModelPricingMode, inference_tools_to_openai_tools, inference_tool_mode_to_openai_tool_choice, openai_tool_calls_to_inference_tool_calls +import inference_gateway.config as config +import utils.logger as logger +from inference_gateway.models import ( + EmbeddingModelInfo, + EmbeddingResult, + InferenceMessage, + InferenceModelInfo, + InferenceResult, + InferenceTool, + InferenceToolMode, + inference_tool_mode_to_openai_tool_choice, + inference_tools_to_openai_tools, + openai_tool_calls_to_inference_tool_calls, +) +from inference_gateway.providers.provider import Provider if config.USE_OPENROUTER: - OPENROUTER_INFERENCE_MODELS_URL = f"{config.OPENROUTER_BASE_URL}/models" # https://openrouter.ai/api/v1/models - OPENROUTER_EMBEDDING_MODELS_URL = f"{config.OPENROUTER_BASE_URL}/embeddings/models" # https://openrouter.ai/api/v1/embeddings/models + OPENROUTER_INFERENCE_MODELS_URL = f"{config.OPENROUTER_BASE_URL}/models" # https://openrouter.ai/api/v1/models + OPENROUTER_EMBEDDING_MODELS_URL = ( + f"{config.OPENROUTER_BASE_URL}/embeddings/models" # https://openrouter.ai/api/v1/embeddings/models + ) class WhitelistedOpenRouterModel(BaseModel): @@ -26,6 +38,7 @@ def __init__(self, **data): if self.openrouter_name is None: self.openrouter_name = self.name + if config.USE_OPENROUTER: WHITELISTED_OPENROUTER_INFERENCE_MODELS = [ WhitelistedOpenRouterModel(name="deepseek-ai/DeepSeek-R1-0528", openrouter_name="deepseek/deepseek-r1-0528"), @@ -58,35 +71,49 @@ async def init(self) -> "OpenRouterProvider": logger.info(f"Fetched {OPENROUTER_INFERENCE_MODELS_URL}") # Add whitelisted inference models - for whitelisted_openrouter_model in WHITELISTED_OPENROUTER_INFERENCE_MODELS: - openrouter_model = next((openrouter_model for openrouter_model in all_openrouter_inference_models_response if openrouter_model["id"] == whitelisted_openrouter_model.openrouter_name), None) + for whitelisted_openrouter_model in WHITELISTED_OPENROUTER_INFERENCE_MODELS: + openrouter_model = next( + ( + openrouter_model + for openrouter_model in all_openrouter_inference_models_response + if openrouter_model["id"] == whitelisted_openrouter_model.openrouter_name + ), + None, + ) if not openrouter_model: - logger.fatal(f"Whitelisted OpenRouter inference model {whitelisted_openrouter_model.openrouter_name} is not supported by OpenRouter") - - if not "text" in openrouter_model["architecture"]["input_modalities"]: - logger.fatal(f"Whitelisted OpenRouter inference model {whitelisted_openrouter_model.chutes_name} does not support text input") - if not "text" in openrouter_model["architecture"]["output_modalities"]: - logger.fatal(f"Whitelisted OpenRouter inference model {whitelisted_openrouter_model.chutes_name} does not support text output") + logger.fatal( + f"Whitelisted OpenRouter inference model {whitelisted_openrouter_model.openrouter_name} is not supported by OpenRouter" + ) + + if "text" not in openrouter_model["architecture"]["input_modalities"]: + logger.fatal( + f"Whitelisted OpenRouter inference model {whitelisted_openrouter_model.chutes_name} does not support text input" + ) + if "text" not in openrouter_model["architecture"]["output_modalities"]: + logger.fatal( + f"Whitelisted OpenRouter inference model {whitelisted_openrouter_model.chutes_name} does not support text output" + ) openrouter_model_pricing = openrouter_model["pricing"] max_input_tokens = openrouter_model["context_length"] cost_usd_per_million_input_tokens = float(openrouter_model_pricing["prompt"]) * 1_000_000 cost_usd_per_million_output_tokens = float(openrouter_model_pricing["completion"]) * 1_000_000 - self.inference_models.append(InferenceModelInfo( - name=whitelisted_openrouter_model.name, - external_name=whitelisted_openrouter_model.openrouter_name, - max_input_tokens=max_input_tokens, - cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, - cost_usd_per_million_output_tokens=cost_usd_per_million_output_tokens - )) + self.inference_models.append( + InferenceModelInfo( + name=whitelisted_openrouter_model.name, + external_name=whitelisted_openrouter_model.openrouter_name, + max_input_tokens=max_input_tokens, + cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, + cost_usd_per_million_output_tokens=cost_usd_per_million_output_tokens, + ) + ) logger.info(f"Found whitelisted OpenRouter inference model {whitelisted_openrouter_model.name}:") logger.info(f" Max input tokens: {max_input_tokens}") logger.info(f" Input cost (USD per million tokens): {cost_usd_per_million_input_tokens}") logger.info(f" Output cost (USD per million tokens): {cost_usd_per_million_output_tokens}") - # Fetch OpenRouter embedding models logger.info(f"Fetching {OPENROUTER_EMBEDDING_MODELS_URL}...") async with httpx.AsyncClient() as client: @@ -97,37 +124,51 @@ async def init(self) -> "OpenRouterProvider": # Add whitelisted embedding models for whitelisted_openrouter_model in WHITELISTED_OPENROUTER_EMBEDDING_MODELS: - openrouter_model = next((openrouter_model for openrouter_model in all_openrouter_embedding_models_response if openrouter_model["id"] == whitelisted_openrouter_model.openrouter_name), None) + openrouter_model = next( + ( + openrouter_model + for openrouter_model in all_openrouter_embedding_models_response + if openrouter_model["id"] == whitelisted_openrouter_model.openrouter_name + ), + None, + ) if not openrouter_model: - logger.fatal(f"Whitelisted OpenRouter embedding model {whitelisted_openrouter_model.openrouter_name} is not supported by OpenRouter") - - if not "text" in openrouter_model["architecture"]["input_modalities"]: - logger.fatal(f"Whitelisted OpenRouter embedding model {whitelisted_openrouter_model.openrouter_name} does not support text input") - if not "embeddings" in openrouter_model["architecture"]["output_modalities"]: # embeddings plural - logger.fatal(f"Whitelisted OpenRouter embedding model {whitelisted_openrouter_model.openrouter_name} does not support embedding output") + logger.fatal( + f"Whitelisted OpenRouter embedding model {whitelisted_openrouter_model.openrouter_name} is not supported by OpenRouter" + ) + + if "text" not in openrouter_model["architecture"]["input_modalities"]: + logger.fatal( + f"Whitelisted OpenRouter embedding model {whitelisted_openrouter_model.openrouter_name} does not support text input" + ) + if "embeddings" not in openrouter_model["architecture"]["output_modalities"]: # embeddings plural + logger.fatal( + f"Whitelisted OpenRouter embedding model {whitelisted_openrouter_model.openrouter_name} does not support embedding output" + ) max_input_tokens = openrouter_model["context_length"] cost_usd_per_million_input_tokens = float(openrouter_model["pricing"]["prompt"]) * 1_000_000 - self.embedding_models.append(EmbeddingModelInfo( - name=whitelisted_openrouter_model.name, - external_name=whitelisted_openrouter_model.openrouter_name, - max_input_tokens=max_input_tokens, - cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens - )) + self.embedding_models.append( + EmbeddingModelInfo( + name=whitelisted_openrouter_model.name, + external_name=whitelisted_openrouter_model.openrouter_name, + max_input_tokens=max_input_tokens, + cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, + ) + ) logger.info(f"Found whitelisted OpenRouter embedding model {whitelisted_openrouter_model.name}:") logger.info(f" Max input tokens: {max_input_tokens}") logger.info(f" Input cost (USD per million tokens): {cost_usd_per_million_input_tokens}") - self.openrouter_client = AsyncOpenAI( base_url=config.OPENROUTER_BASE_URL, api_key=config.OPENROUTER_API_KEY, - default_headers={ # Optional. For rankings on openrouter.ai. - 'HTTP-Referer': 'https://ridges.ai', - 'X-Title': 'Ridges' - } + default_headers={ # Optional. For rankings on openrouter.ai. + "HTTP-Referer": "https://ridges.ai", + "X-Title": "Ridges", + }, ) return self @@ -139,7 +180,7 @@ async def _inference( temperature: float, messages: List[InferenceMessage], tool_mode: InferenceToolMode, - tools: Optional[List[InferenceTool]] + tools: Optional[List[InferenceTool]], ) -> InferenceResult: try: completion_stream: AsyncStream = await self.openrouter_client.chat.completions.create( @@ -149,7 +190,7 @@ async def _inference( tool_choice=inference_tool_mode_to_openai_tool_choice(tool_mode), tools=inference_tools_to_openai_tools(tools) if tools else None, stream=True, - stream_options={"include_usage": True} + stream_options={"include_usage": True}, ) streamed_completion = [] tool_calls = dict() @@ -182,7 +223,8 @@ async def _inference( message_content = "".join(streamed_completion) message_tool_calls = [ - tool_calls[idx] for idx in sorted(tool_calls) # sort by index + tool_calls[idx] + for idx in sorted(tool_calls) # sort by index ] num_input_tokens = last_chunk.usage.prompt_tokens @@ -191,39 +233,27 @@ async def _inference( return InferenceResult( status_code=200, - content=message_content, tool_calls=openai_tool_calls_to_inference_tool_calls(message_tool_calls) if message_tool_calls else [], - num_input_tokens=num_input_tokens, num_output_tokens=num_output_tokens, - cost_usd=cost_usd + cost_usd=cost_usd, ) except APIStatusError as e: # Chutes returned 4xx or 5xx - return InferenceResult( - status_code=e.status_code, - error_message=e.response.text - ) + return InferenceResult(status_code=e.status_code, error_message=e.response.text) except Exception as e: return InferenceResult( - status_code=-1, - error_message=f"Error in OpenRouterProvider._inference(): {type(e).__name__}: {str(e)}" + status_code=-1, error_message=f"Error in OpenRouterProvider._inference(): {type(e).__name__}: {str(e)}" ) - async def _embedding( - self, - *, - model_info: EmbeddingModelInfo, - input: str - ) -> EmbeddingResult: + async def _embedding(self, *, model_info: EmbeddingModelInfo, input: str) -> EmbeddingResult: try: start_time = time() create_embedding_response = await self.openrouter_client.embeddings.create( - model=model_info.external_name, - input=input + model=model_info.external_name, input=input ) end_time = time() @@ -233,23 +263,14 @@ async def _embedding( cost_usd = model_info.get_cost_usd(num_input_tokens, end_time - start_time) return EmbeddingResult( - status_code=200, - - embedding=embedding, - - num_input_tokens=num_input_tokens, - cost_usd=cost_usd + status_code=200, embedding=embedding, num_input_tokens=num_input_tokens, cost_usd=cost_usd ) except APIStatusError as e: # OpenRouter returned 4xx or 5xx - return EmbeddingResult( - status_code=e.status_code, - error_message=e.response.text - ) + return EmbeddingResult(status_code=e.status_code, error_message=e.response.text) except Exception as e: return EmbeddingResult( - status_code=-1, - error_message=f"Error in OpenRouterProvider._embedding(): {type(e).__name__}: {str(e)}" + status_code=-1, error_message=f"Error in OpenRouterProvider._embedding(): {type(e).__name__}: {str(e)}" ) diff --git a/inference_gateway/providers/provider.py b/inference_gateway/providers/provider.py index e6f886791..1092fa099 100644 --- a/inference_gateway/providers/provider.py +++ b/inference_gateway/providers/provider.py @@ -1,18 +1,25 @@ import asyncio -import utils.logger as logger - +from abc import ABC, abstractmethod from http import HTTPStatus from typing import List, Optional -from abc import ABC, abstractmethod -from inference_gateway.models import InferenceTool, EmbeddingResult, InferenceResult, InferenceMessage, InferenceToolMode, EmbeddingModelInfo, InferenceModelInfo, InferenceToolParameter, InferenceToolParameterType - +import utils.logger as logger +from inference_gateway.models import ( + EmbeddingModelInfo, + EmbeddingResult, + InferenceMessage, + InferenceModelInfo, + InferenceResult, + InferenceTool, + InferenceToolMode, + InferenceToolParameter, + InferenceToolParameterType, +) NUM_INFERENCE_CHARS_TO_LOG = 30 NUM_EMBEDDING_CHARS_TO_LOG = 30 - # my_provider = Provider().init() class Provider(ABC): def __init__(self): @@ -21,8 +28,6 @@ def __init__(self): self.inference_models = [] self.embedding_models = [] - - # Abstract methods @abstractmethod @@ -37,21 +42,14 @@ async def _inference( temperature: float, messages: List[InferenceMessage], tool_mode: InferenceToolMode, - tools: Optional[List[InferenceTool]] + tools: Optional[List[InferenceTool]], ) -> InferenceResult: pass @abstractmethod - async def _embedding( - self, - *, - model_info: EmbeddingModelInfo, - input: str - ) -> EmbeddingResult: + async def _embedding(self, *, model_info: EmbeddingModelInfo, input: str) -> EmbeddingResult: pass - - # Inference def is_model_supported_for_inference(self, model_name: str) -> bool: @@ -65,21 +63,25 @@ async def test_inference_model(model_name): response = await self.inference( model_name=model_name, temperature=0.5, - messages=[InferenceMessage(role="user", content="Please use the print(str) tool to say something cool.")], + messages=[ + InferenceMessage(role="user", content="Please use the print(str) tool to say something cool.") + ], tool_mode=InferenceToolMode.REQUIRED, - tools=[InferenceTool( - name="print", - description="Print a string", - parameters=[InferenceToolParameter( - name="str", - description="The string to print", - type=InferenceToolParameterType.STRING - )] - )] + tools=[ + InferenceTool( + name="print", + description="Print a string", + parameters=[ + InferenceToolParameter( + name="str", description="The string to print", type=InferenceToolParameterType.STRING + ) + ], + ) + ], ) return response.status_code == 200 - + logger.info(f"Testing all {self.name} inference models...") tasks = [test_inference_model(model.name) for model in self.inference_models] @@ -88,9 +90,9 @@ async def test_inference_model(model_name): if all(results): logger.info(f"Tested all {self.name} inference models") else: - logger.fatal(f"Failed to test {self.name} inference models: {', '.join([str(model.name) + ' (' + str(model.external_name) + ')' for model, result in zip(self.inference_models, results) if not result])}") - - + logger.fatal( + f"Failed to test {self.name} inference models: {', '.join([str(model.name) + ' (' + str(model.external_name) + ')' for model, result in zip(self.inference_models, results) if not result])}" + ) async def inference( self, @@ -99,25 +101,29 @@ async def inference( temperature: float, messages: List[InferenceMessage], tool_mode: InferenceToolMode = InferenceToolMode.NONE, - tools: Optional[List[InferenceTool]] = None + tools: Optional[List[InferenceTool]] = None, ) -> InferenceResult: # Log the request - request_first_chars = messages[-1].content.replace('\n', '')[:NUM_INFERENCE_CHARS_TO_LOG] if messages else '' - logger.info(f"--> Inference Request {self.name}:{model_name} ({sum(len(message.content) for message in messages)} char(s)): '{request_first_chars}'...") + request_first_chars = messages[-1].content.replace("\n", "")[:NUM_INFERENCE_CHARS_TO_LOG] if messages else "" + logger.info( + f"--> Inference Request {self.name}:{model_name} ({sum(len(message.content) for message in messages)} char(s)): '{request_first_chars}'..." + ) result = await self._inference( model_info=self.get_inference_model_info_by_name(model_name), temperature=temperature, messages=messages, tool_mode=tool_mode, - tools=tools + tools=tools, ) # Log the response if result.status_code == 200: # 200 OK - result_first_chars = result.content.replace('\n', '')[:NUM_INFERENCE_CHARS_TO_LOG] - logger.info(f"<-- Inference Response {self.name}:{model_name} ({len(result.content)} char(s), {len(result.tool_calls)} tool call(s)): '{result_first_chars}'...") + result_first_chars = result.content.replace("\n", "")[:NUM_INFERENCE_CHARS_TO_LOG] + logger.info( + f"<-- Inference Response {self.name}:{model_name} ({len(result.content)} char(s), {len(result.tool_calls)} tool call(s)): '{result_first_chars}'..." + ) elif result.status_code != -1: # 4xx or 5xx result.error_message = f"Inference External Error {self.name}:{model_name}: {result.status_code} {HTTPStatus(result.status_code).phrase}: {result.error_message}" @@ -128,8 +134,6 @@ async def inference( logger.error(f"<-- {result.error_message}") return result - - # Embedding @@ -141,13 +145,10 @@ def get_embedding_model_info_by_name(self, model_name: str) -> Optional[Embeddin async def test_all_embedding_models(self): async def test_embedding_model(model_name): - response = await self.embedding( - model_name=model_name, - input="Hello, world!" - ) + response = await self.embedding(model_name=model_name, input="Hello, world!") return response.status_code == 200 - + logger.info(f"Testing all {self.name} embedding models...") tasks = [test_embedding_model(model.name) for model in self.embedding_models] @@ -156,22 +157,18 @@ async def test_embedding_model(model_name): if all(results): logger.info(f"Tested all {self.name} embedding models") else: - logger.fatal(f"Failed to test {self.name} embedding models: {', '.join([model.name for model, result in zip(self.embedding_models, results) if not result])}") + logger.fatal( + f"Failed to test {self.name} embedding models: {', '.join([model.name for model, result in zip(self.embedding_models, results) if not result])}" + ) - async def embedding( - self, - *, - model_name: str, - input: str - ) -> EmbeddingResult: + async def embedding(self, *, model_name: str, input: str) -> EmbeddingResult: # Log the request - logger.info(f"--> Embedding Request {self.name}:{model_name} ({len(input)} char(s)): '{input[:NUM_INFERENCE_CHARS_TO_LOG]}'...") - - result = await self._embedding( - model_info=self.get_embedding_model_info_by_name(model_name), - input=input + logger.info( + f"--> Embedding Request {self.name}:{model_name} ({len(input)} char(s)): '{input[:NUM_INFERENCE_CHARS_TO_LOG]}'..." ) + result = await self._embedding(model_info=self.get_embedding_model_info_by_name(model_name), input=input) + # Log the response if result.status_code == 200: # 200 OK @@ -185,4 +182,4 @@ async def embedding( result.error_message = f"Embedding Internal Error {self.name}:{model_name}: {result.error_message}" logger.error(f"<-- {result.error_message}") - return result \ No newline at end of file + return result diff --git a/inference_gateway/providers/targon.py b/inference_gateway/providers/targon.py index 824ca7175..6d3435679 100644 --- a/inference_gateway/providers/targon.py +++ b/inference_gateway/providers/targon.py @@ -1,21 +1,30 @@ -import httpx -import utils.logger as logger -import inference_gateway.config as config - from time import time -from pydantic import BaseModel from typing import List, Optional -from openai import AsyncOpenAI, APIStatusError -from inference_gateway.providers.provider import Provider -from inference_gateway.models import InferenceTool, EmbeddingResult, InferenceResult, InferenceMessage, InferenceToolMode, EmbeddingModelInfo, InferenceModelInfo, inference_tools_to_openai_tools, inference_tool_mode_to_openai_tool_choice, openai_tool_calls_to_inference_tool_calls +import httpx +from openai import APIStatusError, AsyncOpenAI +from pydantic import BaseModel +import inference_gateway.config as config +import utils.logger as logger +from inference_gateway.models import ( + EmbeddingModelInfo, + EmbeddingResult, + InferenceMessage, + InferenceModelInfo, + InferenceResult, + InferenceTool, + InferenceToolMode, + inference_tool_mode_to_openai_tool_choice, + inference_tools_to_openai_tools, + openai_tool_calls_to_inference_tool_calls, +) +from inference_gateway.providers.provider import Provider if config.USE_TARGON: TARGON_MODELS_URL = f"{config.TARGON_BASE_URL}/models" - class WhitelistedTargonModel(BaseModel): name: str targon_name: str = None @@ -25,6 +34,7 @@ def __init__(self, **data): if self.targon_name is None: self.targon_name = self.name + WHITELISTED_TARGON_INFERENCE_MODELS = [ WhitelistedTargonModel(name="Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8"), WhitelistedTargonModel(name="Qwen/Qwen3-Next-80B-A3B-Instruct"), @@ -34,104 +44,124 @@ def __init__(self, **data): WhitelistedTargonModel(name="moonshotai/Kimi-K2-Thinking"), ] -WHITELISTED_TARGON_EMBEDDING_MODELS = [ - WhitelistedTargonModel(name="Qwen/Qwen3-Embedding-8B") -] - +WHITELISTED_TARGON_EMBEDDING_MODELS = [WhitelistedTargonModel(name="Qwen/Qwen3-Embedding-8B")] -class TargonProvider(Provider): +class TargonProvider(Provider): async def init(self) -> "TargonProvider": self.name = "Targon" - - # Fetch Targon models logger.info(f"Fetching {TARGON_MODELS_URL}...") async with httpx.AsyncClient() as client: - targon_models_response = await client.get(TARGON_MODELS_URL, headers={"Authorization": f"Bearer {config.TARGON_API_KEY}"}) + targon_models_response = await client.get( + TARGON_MODELS_URL, headers={"Authorization": f"Bearer {config.TARGON_API_KEY}"} + ) targon_models_response.raise_for_status() targon_models = targon_models_response.json()["data"] logger.info(f"Fetched {TARGON_MODELS_URL}") - - # Add whitelisted inference models for whitelisted_targon_model in WHITELISTED_TARGON_INFERENCE_MODELS: - targon_model = next((targon_model for targon_model in targon_models if targon_model["id"] == whitelisted_targon_model.targon_name), None) + targon_model = next( + ( + targon_model + for targon_model in targon_models + if targon_model["id"] == whitelisted_targon_model.targon_name + ), + None, + ) if not targon_model: - logger.fatal(f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} is not supported by Targon") - - if not "text" in targon_model["input_modalities"]: - logger.fatal(f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support text input") - if not "text" in targon_model["output_modalities"]: - logger.fatal(f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support text output") - - if not "CHAT" in targon_model["supported_endpoints"]: - logger.fatal(f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support chat endpoints") - if not "COMPLETION" in targon_model["supported_endpoints"]: - logger.fatal(f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support completion endpoints") + logger.fatal( + f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} is not supported by Targon" + ) + + if "text" not in targon_model["input_modalities"]: + logger.fatal( + f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support text input" + ) + if "text" not in targon_model["output_modalities"]: + logger.fatal( + f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support text output" + ) + + if "CHAT" not in targon_model["supported_endpoints"]: + logger.fatal( + f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support chat endpoints" + ) + if "COMPLETION" not in targon_model["supported_endpoints"]: + logger.fatal( + f"Whitelisted Targon inference model {whitelisted_targon_model.targon_name} does not support completion endpoints" + ) targon_model_pricing = targon_model["pricing"] max_input_tokens = targon_model["context_length"] cost_usd_per_million_input_tokens = float(targon_model_pricing["prompt"]) * 1_000_000 cost_usd_per_million_output_tokens = float(targon_model_pricing["completion"]) * 1_000_000 - self.inference_models.append(InferenceModelInfo( - name=whitelisted_targon_model.name, - external_name=whitelisted_targon_model.targon_name, - max_input_tokens=max_input_tokens, - cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, - cost_usd_per_million_output_tokens=cost_usd_per_million_output_tokens - )) + self.inference_models.append( + InferenceModelInfo( + name=whitelisted_targon_model.name, + external_name=whitelisted_targon_model.targon_name, + max_input_tokens=max_input_tokens, + cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, + cost_usd_per_million_output_tokens=cost_usd_per_million_output_tokens, + ) + ) logger.info(f"Found whitelisted Targon inference model {whitelisted_targon_model.name}:") logger.info(f" Max input tokens: {max_input_tokens}") logger.info(f" Input cost (USD per million tokens): {cost_usd_per_million_input_tokens}") logger.info(f" Output cost (USD per million tokens): {cost_usd_per_million_output_tokens}") - - # Add whitelisted embedding models for whitelisted_targon_model in WHITELISTED_TARGON_EMBEDDING_MODELS: - targon_model = next((targon_model for targon_model in targon_models if targon_model["id"] == whitelisted_targon_model.targon_name), None) + targon_model = next( + ( + targon_model + for targon_model in targon_models + if targon_model["id"] == whitelisted_targon_model.targon_name + ), + None, + ) if not targon_model: - logger.fatal(f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} is not supported by Targon") - - if not "text" in targon_model["input_modalities"]: - logger.fatal(f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} does not support text input") - if not "embedding" in targon_model["output_modalities"]: - logger.fatal(f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} does not support embedding output") - - if not "EMBEDDING" in targon_model["supported_endpoints"]: - logger.fatal(f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} does not support embedding endpoints") + logger.fatal( + f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} is not supported by Targon" + ) + + if "text" not in targon_model["input_modalities"]: + logger.fatal( + f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} does not support text input" + ) + if "embedding" not in targon_model["output_modalities"]: + logger.fatal( + f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} does not support embedding output" + ) + + if "EMBEDDING" not in targon_model["supported_endpoints"]: + logger.fatal( + f"Whitelisted Targon embedding model {whitelisted_targon_model.targon_name} does not support embedding endpoints" + ) max_input_tokens = targon_model["context_length"] cost_usd_per_million_input_tokens = float(targon_model["pricing"]["prompt"]) * 1_000_000 - self.embedding_models.append(EmbeddingModelInfo( - name=whitelisted_targon_model.name, - external_name=whitelisted_targon_model.targon_name, - max_input_tokens=max_input_tokens, - cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens - )) + self.embedding_models.append( + EmbeddingModelInfo( + name=whitelisted_targon_model.name, + external_name=whitelisted_targon_model.targon_name, + max_input_tokens=max_input_tokens, + cost_usd_per_million_input_tokens=cost_usd_per_million_input_tokens, + ) + ) logger.info(f"Found whitelisted Targon embedding model {whitelisted_targon_model.name}:") logger.info(f" Max input tokens: {max_input_tokens}") logger.info(f" Input cost (USD per million tokens): {cost_usd_per_million_input_tokens}") - - - self.targon_client = AsyncOpenAI( - base_url=config.TARGON_BASE_URL, - api_key=config.TARGON_API_KEY - ) - - + self.targon_client = AsyncOpenAI(base_url=config.TARGON_BASE_URL, api_key=config.TARGON_API_KEY) return self - - async def _inference( self, @@ -140,7 +170,7 @@ async def _inference( temperature: float, messages: List[InferenceMessage], tool_mode: InferenceToolMode, - tools: Optional[List[InferenceTool]] + tools: Optional[List[InferenceTool]], ) -> InferenceResult: try: chat_completion = await self.targon_client.chat.completions.create( @@ -149,7 +179,7 @@ async def _inference( messages=messages, tool_choice=inference_tool_mode_to_openai_tool_choice(tool_mode), tools=inference_tools_to_openai_tools(tools) if tools else None, - stream=False + stream=False, ) message = chat_completion.choices[0].message @@ -160,41 +190,27 @@ async def _inference( return InferenceResult( status_code=200, - content=message.content if message.content else "", tool_calls=openai_tool_calls_to_inference_tool_calls(message.tool_calls) if message.tool_calls else [], - num_input_tokens=num_input_tokens, num_output_tokens=num_output_tokens, - cost_usd=cost_usd + cost_usd=cost_usd, ) except APIStatusError as e: # Targon returned 4xx or 5xx - return InferenceResult( - status_code=e.status_code, - error_message=e.response.text - ) + return InferenceResult(status_code=e.status_code, error_message=e.response.text) except Exception as e: return InferenceResult( - status_code=-1, - error_message=f"Error in TargonProvider._inference(): {type(e).__name__}: {str(e)}" + status_code=-1, error_message=f"Error in TargonProvider._inference(): {type(e).__name__}: {str(e)}" ) - - - async def _embedding( - self, - *, - model_info: EmbeddingModelInfo, - input: str - ) -> EmbeddingResult: + async def _embedding(self, *, model_info: EmbeddingModelInfo, input: str) -> EmbeddingResult: try: start_time = time() create_embedding_response = await self.targon_client.embeddings.create( - model=model_info.external_name, - input=input + model=model_info.external_name, input=input ) end_time = time() @@ -204,23 +220,14 @@ async def _embedding( cost_usd = model_info.get_cost_usd(num_input_tokens, end_time - start_time) return EmbeddingResult( - status_code=200, - - embedding=embedding, - - num_input_tokens=num_input_tokens, - cost_usd=cost_usd + status_code=200, embedding=embedding, num_input_tokens=num_input_tokens, cost_usd=cost_usd ) except APIStatusError as e: # Targon returned 4xx or 5xx - return EmbeddingResult( - status_code=e.status_code, - error_message=e.response.text - ) + return EmbeddingResult(status_code=e.status_code, error_message=e.response.text) except Exception as e: return EmbeddingResult( - status_code=-1, - error_message=f"Error in TargonProvider._embedding(): {type(e).__name__}: {str(e)}" - ) \ No newline at end of file + status_code=-1, error_message=f"Error in TargonProvider._embedding(): {type(e).__name__}: {str(e)}" + ) diff --git a/models/agent.py b/models/agent.py index f05aac15e..33d940012 100644 --- a/models/agent.py +++ b/models/agent.py @@ -1,19 +1,18 @@ +from datetime import datetime from enum import Enum -from uuid import UUID from typing import Optional -from datetime import datetime -from pydantic import BaseModel +from uuid import UUID +from pydantic import BaseModel class AgentStatus(str, Enum): - screening_1 = 'screening_1' - failed_screening_1 = 'failed_screening_1' - screening_2 = 'screening_2' - failed_screening_2 = 'failed_screening_2' - evaluating = 'evaluating' - finished = 'finished' - + screening_1 = "screening_1" + failed_screening_1 = "failed_screening_1" + screening_2 = "screening_2" + failed_screening_2 = "failed_screening_2" + evaluating = "evaluating" + finished = "finished" class Agent(BaseModel): @@ -28,24 +27,15 @@ class Agent(BaseModel): created_at: datetime ip_address: Optional[str] = None + class PossiblyBenchmarkAgent(Agent): is_benchmark_agent: bool benchmark_description: Optional[str] = None - - - - - - - - - - - # TODO ADAM: need to look into this more + class BenchmarkAgentScored(Agent): benchmark_description: Optional[str] = None @@ -53,8 +43,10 @@ class BenchmarkAgentScored(Agent): approved: bool validator_count: int final_score: float + + class AgentScored(Agent): set_id: int approved: bool validator_count: int - final_score: float \ No newline at end of file + final_score: float diff --git a/models/banned_hotkey.py b/models/banned_hotkey.py index aa920ee3f..109f5e281 100644 --- a/models/banned_hotkey.py +++ b/models/banned_hotkey.py @@ -1,6 +1,6 @@ from datetime import datetime -from pydantic import BaseModel +from pydantic import BaseModel class BannedHotkey(BaseModel): diff --git a/models/evaluation.py b/models/evaluation.py index 31ad1ed96..1163cecaa 100644 --- a/models/evaluation.py +++ b/models/evaluation.py @@ -1,18 +1,18 @@ +from datetime import datetime from enum import Enum -from uuid import UUID from typing import Optional -from datetime import datetime +from uuid import UUID + from pydantic import BaseModel + from models.evaluation_run import EvaluationRun from models.evaluation_set import EvaluationSetGroup - class EvaluationStatus(str, Enum): - success = 'success' - running = 'running' - failure = 'failure' - + success = "success" + running = "running" + failure = "failure" class Evaluation(BaseModel): @@ -24,11 +24,11 @@ class Evaluation(BaseModel): created_at: datetime finished_at: Optional[datetime] = None + class EvaluationWithRuns(Evaluation): runs: list[EvaluationRun] - class HydratedEvaluation(Evaluation): status: EvaluationStatus - score: float \ No newline at end of file + score: float diff --git a/models/evaluation_run.py b/models/evaluation_run.py index 891c28f7c..d2e539aa6 100644 --- a/models/evaluation_run.py +++ b/models/evaluation_run.py @@ -1,10 +1,11 @@ -from uuid import UUID from datetime import datetime from enum import Enum, IntEnum -from pydantic import BaseModel from typing import List, Optional -from models.problem import ProblemTestResult +from uuid import UUID +from pydantic import BaseModel + +from models.problem import ProblemTestResult class EvaluationRunErrorCode(IntEnum): @@ -14,36 +15,63 @@ def __new__(cls, code: int, message: str): obj._value_ = code obj.message = message return obj - + # 1xxx - Agent Errors AGENT_EXCEPTION_RUNNING_AGENT = (1000, "The agent raised an exception while being run") - AGENT_EXCEPTION_RUNNING_EVAL = (1010, "The agent raised an exception while being evaluated") - AGENT_TIMEOUT_RUNNING_AGENT = (1020, "The agent timed out while being run") - AGENT_TIMEOUT_RUNNING_EVAL = (1030, "The agent timed out while being evaluated") - AGENT_INVALID_PATCH = (1040, "The agent returned an invalid patch") + AGENT_EXCEPTION_RUNNING_EVAL = (1010, "The agent raised an exception while being evaluated") + AGENT_TIMEOUT_RUNNING_AGENT = (1020, "The agent timed out while being run") + AGENT_TIMEOUT_RUNNING_EVAL = (1030, "The agent timed out while being evaluated") + AGENT_INVALID_PATCH = (1040, "The agent returned an invalid patch") # 2xxx - Validator Errors - VALIDATOR_INTERNAL_ERROR = (2000, "An internal error occurred on the validator") - VALIDATOR_FAILED_PENDING = (2010, "An internal error occurred on the validator while the evaluation run was pending") - VALIDATOR_FAILED_INIT_AGENT = (2020, "An internal error occurred on the validator while the evaluation run was initializing the agent") - VALIDATOR_FAILED_RUNNING_AGENT = (2030, "An internal error occurred on the validator while the evaluation run was running the agent") - VALIDATOR_FAILED_INIT_EVAL = (2040, "An internal error occurred on the validator while the evaluation run was initializing the evaluation") - VALIDATOR_FAILED_RUNNING_EVAL = (2050, "An internal error occurred on the validator while the evaluation run was running the evaluation") - VALIDATOR_UNKNOWN_PROBLEM = (2060, "Unknown problem") + VALIDATOR_INTERNAL_ERROR = (2000, "An internal error occurred on the validator") + VALIDATOR_FAILED_PENDING = ( + 2010, + "An internal error occurred on the validator while the evaluation run was pending", + ) + VALIDATOR_FAILED_INIT_AGENT = ( + 2020, + "An internal error occurred on the validator while the evaluation run was initializing the agent", + ) + VALIDATOR_FAILED_RUNNING_AGENT = ( + 2030, + "An internal error occurred on the validator while the evaluation run was running the agent", + ) + VALIDATOR_FAILED_INIT_EVAL = ( + 2040, + "An internal error occurred on the validator while the evaluation run was initializing the evaluation", + ) + VALIDATOR_FAILED_RUNNING_EVAL = ( + 2050, + "An internal error occurred on the validator while the evaluation run was running the evaluation", + ) + VALIDATOR_UNKNOWN_PROBLEM = (2060, "Unknown problem") # 3xxx - Platform Errors - PLATFORM_RESTARTED_WHILE_PENDING = (3000, "The platform was restarted while the evaluation run was pending") - PLATFORM_RESTARTED_WHILE_INIT_AGENT = (3010, "The platform was restarted while the evaluation run was initializing the agent") - PLATFORM_RESTARTED_WHILE_RUNNING_AGENT = (3020, "The platform was restarted while the evaluation run was running the agent") - PLATFORM_RESTARTED_WHILE_INIT_EVAL = (3030, "The platform was restarted while the evaluation run was initializing the evaluation") - PLATFORM_RESTARTED_WHILE_RUNNING_EVAL = (3040, "The platform was restarted while the evaluation run was running the evaluation") + PLATFORM_RESTARTED_WHILE_PENDING = (3000, "The platform was restarted while the evaluation run was pending") + PLATFORM_RESTARTED_WHILE_INIT_AGENT = ( + 3010, + "The platform was restarted while the evaluation run was initializing the agent", + ) + PLATFORM_RESTARTED_WHILE_RUNNING_AGENT = ( + 3020, + "The platform was restarted while the evaluation run was running the agent", + ) + PLATFORM_RESTARTED_WHILE_INIT_EVAL = ( + 3030, + "The platform was restarted while the evaluation run was initializing the evaluation", + ) + PLATFORM_RESTARTED_WHILE_RUNNING_EVAL = ( + 3040, + "The platform was restarted while the evaluation run was running the evaluation", + ) def get_error_message(self) -> str: return self.message - + def is_agent_error(self) -> bool: return 1000 <= self.value < 2000 - + def is_validator_error(self) -> bool: return 2000 <= self.value < 3000 @@ -51,16 +79,14 @@ def is_platform_error(self) -> bool: return 3000 <= self.value < 4000 - class EvaluationRunStatus(str, Enum): - pending = 'pending' - initializing_agent = 'initializing_agent' - running_agent = 'running_agent' - initializing_eval = 'initializing_eval' - running_eval = 'running_eval' - finished = 'finished' - error = 'error' - + pending = "pending" + initializing_agent = "initializing_agent" + running_agent = "running_agent" + initializing_eval = "initializing_eval" + running_eval = "running_eval" + finished = "finished" + error = "error" class EvaluationRun(BaseModel): @@ -84,7 +110,6 @@ class EvaluationRun(BaseModel): finished_or_errored_at: Optional[datetime] = None - class EvaluationRunLogType(str, Enum): - agent = 'agent' - eval = 'eval' \ No newline at end of file + agent = "agent" + eval = "eval" diff --git a/models/evaluation_set.py b/models/evaluation_set.py index 52ffc9e00..5725df03a 100644 --- a/models/evaluation_set.py +++ b/models/evaluation_set.py @@ -1,8 +1,7 @@ import datetime - from enum import Enum -from pydantic import BaseModel +from pydantic import BaseModel class EvaluationSetGroup(str, Enum): @@ -20,9 +19,8 @@ def from_validator_hotkey(validator_hotkey: str) -> "EvaluationSetGroup": return EvaluationSetGroup.validator - class EvaluationSetProblem(BaseModel): set_id: int set_group: EvaluationSetGroup problem_name: str - created_at: datetime.datetime \ No newline at end of file + created_at: datetime.datetime diff --git a/models/payments.py b/models/payments.py index 9a265646f..f9b0d2795 100644 --- a/models/payments.py +++ b/models/payments.py @@ -1,7 +1,9 @@ -from uuid import UUID from datetime import datetime +from uuid import UUID + from pydantic import BaseModel + class Payment(BaseModel): payment_block_hash: str payment_extrinsic_index: str @@ -12,4 +14,4 @@ class Payment(BaseModel): miner_coldkey: str amount_rao: int - created_at: datetime \ No newline at end of file + created_at: datetime diff --git a/models/problem.py b/models/problem.py index b8211269f..64a89d4a8 100644 --- a/models/problem.py +++ b/models/problem.py @@ -1,24 +1,25 @@ from enum import Enum -from pydantic import BaseModel from typing import Any, Optional +from pydantic import BaseModel class ProblemTestCategory(str, Enum): - default = 'default' - pass_to_pass = 'pass_to_pass' - fail_to_pass = 'fail_to_pass' + default = "default" + pass_to_pass = "pass_to_pass" + fail_to_pass = "fail_to_pass" + class ProblemTest(BaseModel): name: str category: ProblemTestCategory - class ProblemTestResultStatus(str, Enum): - PASS = 'pass' - FAIL = 'fail' - SKIP = 'skip' + PASS = "pass" + FAIL = "fail" + SKIP = "skip" + class ProblemTestResult(BaseModel): name: str @@ -26,12 +27,12 @@ class ProblemTestResult(BaseModel): status: ProblemTestResultStatus - class ProblemDifficulty(str, Enum): - EASY = 'easy' - MEDIUM = 'medium' - HARD = 'hard' - IMPOSSIBLE = 'impossible' + EASY = "easy" + MEDIUM = "medium" + HARD = "hard" + IMPOSSIBLE = "impossible" + class Problem(BaseModel): name: str @@ -40,4 +41,4 @@ class Problem(BaseModel): problem_statement: str solution_diff: str - userdata: Any = None \ No newline at end of file + userdata: Any = None diff --git a/pyproject.toml b/pyproject.toml index e2823a350..d901af81b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,19 +25,21 @@ dependencies = [ "uvicorn>=0.30.5", ] -# Python formatting and linting configuration -[tool.black] -line-length = 150 +[project.optional-dependencies] +dev = [ + "ruff==0.15.7", +] +# Python formatting and linting configuration [tool.flake8] max-line-length = 120 [tool.ruff] line-length = 120 -[tool.isort] -line_length = 120 -profile = "black" +[tool.ruff.lint] +extend-select = ["I"] # isort [tool.setuptools] py-modules = [] + diff --git a/queries/agent.py b/queries/agent.py index 3d17382d0..b0b2fa15e 100644 --- a/queries/agent.py +++ b/queries/agent.py @@ -1,15 +1,14 @@ -import api.config as config -import utils.logger as logger - -from uuid import UUID from datetime import datetime from typing import List, Optional -from utils.s3 import upload_text_file_to_s3 +from uuid import UUID + +import api.config as config +import utils.logger as logger +from models.agent import Agent, AgentScored, AgentStatus, BenchmarkAgentScored, PossiblyBenchmarkAgent from models.evaluation import EvaluationStatus from models.evaluation_set import EvaluationSetGroup -from utils.database import db_operation, DatabaseConnection -from models.agent import Agent, AgentStatus, AgentScored, BenchmarkAgentScored, PossiblyBenchmarkAgent - +from utils.database import DatabaseConnection, db_operation +from utils.s3 import upload_text_file_to_s3 @db_operation @@ -21,7 +20,7 @@ async def get_agent_by_id(conn: DatabaseConnection, agent_id: UUID) -> Optional[ WHERE agent_id = $1 LIMIT 1 """, - agent_id + agent_id, ) if result is None: @@ -29,8 +28,11 @@ async def get_agent_by_id(conn: DatabaseConnection, agent_id: UUID) -> Optional[ return Agent(**result) + @db_operation -async def get_possibly_benchmark_agent_by_id(conn: DatabaseConnection, agent_id: UUID) -> Optional[PossiblyBenchmarkAgent]: +async def get_possibly_benchmark_agent_by_id( + conn: DatabaseConnection, agent_id: UUID +) -> Optional[PossiblyBenchmarkAgent]: result = await conn.fetchrow( """ SELECT @@ -42,14 +44,13 @@ async def get_possibly_benchmark_agent_by_id(conn: DatabaseConnection, agent_id: WHERE a.agent_id = $1 LIMIT 1 """, - agent_id + agent_id, ) - + if result is None: return None - - return PossiblyBenchmarkAgent(**result) + return PossiblyBenchmarkAgent(**result) @db_operation @@ -63,16 +64,15 @@ async def get_agent_by_evaluation_run_id(conn: DatabaseConnection, evaluation_ru ) LIMIT 1 ) """, - evaluation_run_id + evaluation_run_id, ) - + if result is None: return None return Agent(**result) - @db_operation async def get_all_agents_by_miner_hotkey(conn: DatabaseConnection, miner_hotkey: str) -> List[Agent]: result = await conn.fetch( @@ -81,11 +81,10 @@ async def get_all_agents_by_miner_hotkey(conn: DatabaseConnection, miner_hotkey: WHERE miner_hotkey = $1 ORDER BY created_at DESC """, - miner_hotkey + miner_hotkey, ) - - return [Agent(**agent) for agent in result] + return [Agent(**agent) for agent in result] @db_operation @@ -97,18 +96,19 @@ async def get_latest_agent_for_miner_hotkey(conn: DatabaseConnection, miner_hotk ORDER BY created_at DESC LIMIT 1 """, - miner_hotkey + miner_hotkey, ) if result is None: - return None - - return Agent(**result) + return None + return Agent(**result) @db_operation -async def get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id(conn: DatabaseConnection, miner_hotkey: str) -> Optional[datetime]: +async def get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id( + conn: DatabaseConnection, miner_hotkey: str +) -> Optional[datetime]: result = await conn.fetchval( """ SELECT MAX(a.created_at) @@ -116,11 +116,10 @@ async def get_latest_agent_created_at_for_miner_hotkey_in_latest_set_id(conn: Da WHERE a.miner_hotkey = $1 AND a.created_at > (SELECT MAX(created_at) FROM evaluation_sets) """, - miner_hotkey + miner_hotkey, ) - - return result + return result @db_operation @@ -140,7 +139,6 @@ async def create_agent(conn: DatabaseConnection, agent: Agent, agent_text: str) ) - @db_operation async def update_agent_status(conn: DatabaseConnection, agent_id: UUID, status: AgentStatus) -> None: await conn.execute( @@ -150,11 +148,10 @@ async def update_agent_status(conn: DatabaseConnection, agent_id: UUID, status: WHERE agent_id = $1 """, agent_id, - status.value + status.value, ) - @db_operation async def get_benchmark_agents(conn: DatabaseConnection) -> List[BenchmarkAgentScored]: result = await conn.fetch( @@ -172,45 +169,41 @@ async def get_benchmark_agents(conn: DatabaseConnection) -> List[BenchmarkAgentS return [BenchmarkAgentScored(**agent) for agent in result] - - - - - - - - - # TODO ADAM: fix this section + @db_operation async def record_upload_attempt(conn: DatabaseConnection, upload_type: str, success: bool, **kwargs) -> None: # TODO ADAM: gross - """Record an upload attempt in the upload_attempts table.""" try: await conn.execute( """INSERT INTO upload_attempts (upload_type, success, hotkey, agent_name, filename, file_size_bytes, ip_address, error_type, error_message, ban_reason, http_status_code, agent_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)""", - upload_type, success, kwargs.get('hotkey'), kwargs.get('agent_name'), kwargs.get('filename'), - kwargs.get('file_size_bytes'), kwargs.get('ip_address'), kwargs.get('error_type'), - kwargs.get('error_message'), kwargs.get('ban_reason'), kwargs.get('http_status_code'), kwargs.get('agent_id') + upload_type, + success, + kwargs.get("hotkey"), + kwargs.get("agent_name"), + kwargs.get("filename"), + kwargs.get("file_size_bytes"), + kwargs.get("ip_address"), + kwargs.get("error_type"), + kwargs.get("error_message"), + kwargs.get("ban_reason"), + kwargs.get("http_status_code"), + kwargs.get("agent_id"), + ) + logger.debug( + f"Recorded upload attempt: type={upload_type}, success={success}, error_type={kwargs.get('error_type')}" ) - logger.debug(f"Recorded upload attempt: type={upload_type}, success={success}, error_type={kwargs.get('error_type')}") except Exception as e: logger.error(f"Failed to record upload attempt: {e}") - - @db_operation -async def get_top_agents( - conn: DatabaseConnection, - number_of_agents: int = 10, - page: int = 1 -) -> list[AgentScored]: +async def get_top_agents(conn: DatabaseConnection, number_of_agents: int = 10, page: int = 1) -> list[AgentScored]: # TODO ADAM: this query was supposed to be fixed to remove the pagination concept # TODO ADAM: maybe edge case bugs here if pagenum is 0,negative,or too high etc offset = (page - 1) * number_of_agents @@ -222,7 +215,9 @@ async def get_top_agents( and agent_id not in (select agent_id from benchmark_agent_ids) order by round(final_score::numeric, 6) desc, created_at asc limit $1 offset $2 - """, number_of_agents, offset + """, + number_of_agents, + offset, ) return [AgentScored(**agent) for agent in results] @@ -252,8 +247,11 @@ async def get_agents_in_queue(conn: DatabaseConnection, queue_stage: EvaluationS return [Agent(**agent) for agent in queue] + @db_operation -async def get_next_agent_id_awaiting_evaluation_for_validator_hotkey(conn: DatabaseConnection, validator_hotkey: str) -> Optional[UUID]: +async def get_next_agent_id_awaiting_evaluation_for_validator_hotkey( + conn: DatabaseConnection, validator_hotkey: str +) -> Optional[UUID]: if validator_hotkey.startswith("screener-1"): result = await conn.fetchrow(""" SELECT agent_id FROM screener_1_queue LIMIT 1 @@ -310,11 +308,10 @@ async def get_next_agent_id_awaiting_evaluation_for_validator_hotkey(conn: Datab LIMIT 1 """, validator_hotkey, - config.NUM_EVALS_PER_AGENT + config.NUM_EVALS_PER_AGENT, ) if result is None: return None return result["agent_id"] - diff --git a/queries/banned_hotkey.py b/queries/banned_hotkey.py index 8332e6cbd..a476b7ae6 100644 --- a/queries/banned_hotkey.py +++ b/queries/banned_hotkey.py @@ -1,8 +1,7 @@ from typing import Optional from models.banned_hotkey import BannedHotkey -from utils.database import db_operation, DatabaseConnection - +from utils.database import DatabaseConnection, db_operation @db_operation @@ -11,7 +10,7 @@ async def get_banned_hotkey(conn: DatabaseConnection, miner_hotkey: str) -> Opti """ SELECT * FROM banned_hotkeys WHERE miner_hotkey = $1 """, - miner_hotkey + miner_hotkey, ) if not banned_hotkey: diff --git a/queries/embedding.py b/queries/embedding.py index 56e024da2..68dace732 100644 --- a/queries/embedding.py +++ b/queries/embedding.py @@ -1,25 +1,17 @@ import json - -from uuid import UUID, uuid4 from typing import Any, List, Optional -from utils.database import db_operation, DatabaseConnection +from uuid import UUID, uuid4 +from utils.database import DatabaseConnection, db_operation def _remove_null_bytes(x: Any) -> Any: - return x.replace('\x00', '') - + return x.replace("\x00", "") @db_operation async def create_new_embedding( - conn: DatabaseConnection, - *, - evaluation_run_id: UUID, - - provider: str, - model: str, - input: str + conn: DatabaseConnection, *, evaluation_run_id: UUID, provider: str, model: str, input: str ) -> UUID: embedding_id = uuid4() @@ -39,7 +31,6 @@ async def create_new_embedding( """, embedding_id, evaluation_run_id, - provider, model, _remove_null_bytes(input), @@ -48,17 +39,15 @@ async def create_new_embedding( return embedding_id - @db_operation async def update_embedding_by_id( conn: DatabaseConnection, *, embedding_id: UUID, - status_code: Optional[int] = None, response: Optional[List[float]] = None, num_input_tokens: Optional[int] = None, - cost_usd: Optional[float] = None + cost_usd: Optional[float] = None, ) -> None: await conn.execute( """ @@ -76,5 +65,5 @@ async def update_embedding_by_id( status_code, json.dumps(response), num_input_tokens, - cost_usd - ) \ No newline at end of file + cost_usd, + ) diff --git a/queries/evaluation.py b/queries/evaluation.py index b5a082908..c58574432 100644 --- a/queries/evaluation.py +++ b/queries/evaluation.py @@ -1,14 +1,13 @@ -import utils.logger as logger - +from typing import List, Optional, Tuple from uuid import UUID, uuid4 -from typing import List, Tuple, Optional -from models.evaluation_set import EvaluationSetGroup -from utils.database import db_operation, DatabaseConnection + +import utils.logger as logger from models.evaluation import Evaluation, EvaluationStatus, HydratedEvaluation -from models.evaluation_run import EvaluationRun, EvaluationRunStatus, EvaluationRunErrorCode -from queries.evaluation_set import get_latest_set_id, get_all_problem_names_in_set_group_in_set_id +from models.evaluation_run import EvaluationRun, EvaluationRunErrorCode, EvaluationRunStatus +from models.evaluation_set import EvaluationSetGroup from queries.evaluation_run import create_evaluation_runs, get_all_evaluation_runs_in_evaluation_id - +from queries.evaluation_set import get_all_problem_names_in_set_group_in_set_id, get_latest_set_id +from utils.database import DatabaseConnection, db_operation @db_operation @@ -30,39 +29,39 @@ async def create_evaluation(conn: DatabaseConnection, agent_id: UUID, validator_ agent_id, validator_hotkey, set_id, - EvaluationSetGroup.from_validator_hotkey(validator_hotkey).value # TODO ADAM + EvaluationSetGroup.from_validator_hotkey(validator_hotkey).value, # TODO ADAM ) - logger.debug(f"Created evaluation {evaluation_id} for agent {agent_id} with validator hotkey {validator_hotkey} and set ID {set_id}") + logger.debug( + f"Created evaluation {evaluation_id} for agent {agent_id} with validator hotkey {validator_hotkey} and set ID {set_id}" + ) return evaluation_id - @db_operation -async def create_new_evaluation_and_evaluation_runs(conn: DatabaseConnection, agent_id: UUID, validator_hotkey: str, set_id: int = None) -> Tuple[Evaluation, List[EvaluationRun]]: +async def create_new_evaluation_and_evaluation_runs( + conn: DatabaseConnection, agent_id: UUID, validator_hotkey: str, set_id: int = None +) -> Tuple[Evaluation, List[EvaluationRun]]: if set_id is None: set_id = await get_latest_set_id() - - logger.debug(f"Creating new evaluation and evaluation runs for agent {agent_id} with validator hotkey {validator_hotkey} and set ID {set_id}") + + logger.debug( + f"Creating new evaluation and evaluation runs for agent {agent_id} with validator hotkey {validator_hotkey} and set ID {set_id}" + ) set_group = EvaluationSetGroup.from_validator_hotkey(validator_hotkey) problem_names = await get_all_problem_names_in_set_group_in_set_id(set_id, set_group) logger.debug(f"# of problems in set ID {set_id}, set group {set_group.value}: {len(problem_names)}") - evaluation_id = await create_evaluation( - agent_id, - validator_hotkey, - set_id - ) + evaluation_id = await create_evaluation(agent_id, validator_hotkey, set_id) await create_evaluation_runs(evaluation_id, problem_names) return await get_evaluation_by_id(evaluation_id), await get_all_evaluation_runs_in_evaluation_id(evaluation_id) - @db_operation async def get_evaluation_by_id(conn: DatabaseConnection, evaluation_id: UUID) -> Evaluation: response = await conn.fetchrow( @@ -77,7 +76,6 @@ async def get_evaluation_by_id(conn: DatabaseConnection, evaluation_id: UUID) -> return Evaluation(**response) - @db_operation async def get_hydrated_evaluation_by_id(conn: DatabaseConnection, evaluation_id: UUID) -> Optional[HydratedEvaluation]: result = await conn.fetchrow( @@ -95,9 +93,10 @@ async def get_hydrated_evaluation_by_id(conn: DatabaseConnection, evaluation_id: return HydratedEvaluation(**result) - @db_operation -async def get_hydrated_evaluation_by_evaluation_run_id(conn: DatabaseConnection, evaluation_run_id: UUID) -> Optional[HydratedEvaluation]: +async def get_hydrated_evaluation_by_evaluation_run_id( + conn: DatabaseConnection, evaluation_run_id: UUID +) -> Optional[HydratedEvaluation]: result = await conn.fetchrow( """ SELECT * @@ -109,9 +108,8 @@ async def get_hydrated_evaluation_by_evaluation_run_id(conn: DatabaseConnection, if result is None: return None - - return HydratedEvaluation(**result) + return HydratedEvaluation(**result) @db_operation @@ -122,13 +120,12 @@ async def get_evaluations_for_agent_id(conn: DatabaseConnection, agent_id: UUID) FROM evaluations WHERE agent_id = $1 """, - agent_id + agent_id, ) return [Evaluation(**evaluation) for evaluation in results] - @db_operation async def update_evaluation_finished_at(conn: DatabaseConnection, evaluation_id: UUID) -> None: await conn.execute( @@ -137,11 +134,10 @@ async def update_evaluation_finished_at(conn: DatabaseConnection, evaluation_id: SET finished_at = NOW() WHERE evaluation_id = $1 """, - evaluation_id + evaluation_id, ) - @db_operation async def get_num_successful_validator_evaluations_for_agent_id(conn: DatabaseConnection, agent_id: UUID) -> int: return await conn.fetchval( @@ -153,16 +149,13 @@ async def get_num_successful_validator_evaluations_for_agent_id(conn: DatabaseCo AND status = '{EvaluationStatus.success.value}' AND evaluation_set_group = '{EvaluationSetGroup.validator.value}'::EvaluationSetGroup """, - agent_id + agent_id, ) - - - - # TODO ADAM: Fix this section + @db_operation async def set_all_unfinished_evaluation_runs_to_errored(conn: DatabaseConnection, error_message: str) -> None: await conn.execute( @@ -183,11 +176,14 @@ async def set_all_unfinished_evaluation_runs_to_errored(conn: DatabaseConnection WHERE status NOT IN ('{EvaluationRunStatus.finished.value}', '{EvaluationRunStatus.error.value}') """, - error_message + error_message, ) + @db_operation -async def update_unfinished_evaluation_runs_in_evaluation_id_to_errored(conn: DatabaseConnection, evaluation_id: UUID, error_message: str) -> None: +async def update_unfinished_evaluation_runs_in_evaluation_id_to_errored( + conn: DatabaseConnection, evaluation_id: UUID, error_message: str +) -> None: await conn.execute( f""" UPDATE evaluation_runs @@ -206,5 +202,5 @@ async def update_unfinished_evaluation_runs_in_evaluation_id_to_errored(conn: Da AND status NOT IN ('{EvaluationRunStatus.finished.value}', '{EvaluationRunStatus.error.value}') """, evaluation_id, - error_message + error_message, ) diff --git a/queries/evaluation_run.py b/queries/evaluation_run.py index cf11deabe..40eb8a239 100644 --- a/queries/evaluation_run.py +++ b/queries/evaluation_run.py @@ -1,12 +1,12 @@ -import asyncpg import json -import utils.logger as logger - -from uuid import UUID, uuid4 from typing import List, Optional -from utils.database import db_operation, DatabaseConnection -from models.evaluation_run import EvaluationRun, EvaluationRunStatus, EvaluationRunLogType +from uuid import UUID, uuid4 + +import asyncpg +import utils.logger as logger +from models.evaluation_run import EvaluationRun, EvaluationRunLogType, EvaluationRunStatus +from utils.database import DatabaseConnection, db_operation def _parse_evaluation_run_from_row(row: asyncpg.Record) -> EvaluationRun: @@ -17,7 +17,6 @@ def _parse_evaluation_run_from_row(row: asyncpg.Record) -> EvaluationRun: return EvaluationRun(**row_dict) - @db_operation async def get_evaluation_run_by_id(conn: DatabaseConnection, evaluation_run_id: UUID) -> Optional[EvaluationRun]: row = await conn.fetchrow( @@ -26,7 +25,7 @@ async def get_evaluation_run_by_id(conn: DatabaseConnection, evaluation_run_id: FROM evaluation_runs WHERE evaluation_run_id = $1 """, - evaluation_run_id + evaluation_run_id, ) if not row: @@ -35,14 +34,16 @@ async def get_evaluation_run_by_id(conn: DatabaseConnection, evaluation_run_id: return _parse_evaluation_run_from_row(row) - @db_operation -async def get_evaluation_run_status_by_id(conn: DatabaseConnection, evaluation_run_id: UUID) -> Optional[EvaluationRunStatus]: +async def get_evaluation_run_status_by_id( + conn: DatabaseConnection, evaluation_run_id: UUID +) -> Optional[EvaluationRunStatus]: status = await conn.fetchval( """ SELECT status FROM evaluation_runs WHERE evaluation_run_id = $1 """, - evaluation_run_id) + evaluation_run_id, + ) if status is None: return None @@ -50,22 +51,22 @@ async def get_evaluation_run_status_by_id(conn: DatabaseConnection, evaluation_r return EvaluationRunStatus(status) - @db_operation -async def get_all_evaluation_runs_in_evaluation_id(conn: DatabaseConnection, evaluation_id: UUID) -> List[EvaluationRun]: +async def get_all_evaluation_runs_in_evaluation_id( + conn: DatabaseConnection, evaluation_id: UUID +) -> List[EvaluationRun]: rows = await conn.fetch( """ SELECT * FROM evaluation_runs WHERE evaluation_id = $1 """, - evaluation_id + evaluation_id, ) return [_parse_evaluation_run_from_row(row) for row in rows] - @db_operation async def update_evaluation_run_by_id(conn: DatabaseConnection, evaluation_run: EvaluationRun) -> None: await conn.execute( @@ -86,18 +87,19 @@ async def update_evaluation_run_by_id(conn: DatabaseConnection, evaluation_run: evaluation_run.evaluation_run_id, evaluation_run.status.value, evaluation_run.patch, - json.dumps([test_result.model_dump() for test_result in evaluation_run.test_results]) if evaluation_run.test_results else None, + json.dumps([test_result.model_dump() for test_result in evaluation_run.test_results]) + if evaluation_run.test_results + else None, evaluation_run.error_code, evaluation_run.error_message, evaluation_run.started_initializing_agent_at, evaluation_run.started_running_agent_at, evaluation_run.started_initializing_eval_at, evaluation_run.started_running_eval_at, - evaluation_run.finished_or_errored_at + evaluation_run.finished_or_errored_at, ) - @db_operation async def create_evaluation_run(conn: DatabaseConnection, evaluation_id: UUID, problem_name: str) -> UUID: evaluation_run_id = uuid4() @@ -115,15 +117,16 @@ async def create_evaluation_run(conn: DatabaseConnection, evaluation_id: UUID, p evaluation_run_id, evaluation_id, problem_name, - EvaluationRunStatus.pending.value + EvaluationRunStatus.pending.value, ) - logger.debug(f"Created evaluation run {evaluation_run_id} for evaluation {evaluation_id} with problem name {problem_name}") + logger.debug( + f"Created evaluation run {evaluation_run_id} for evaluation {evaluation_id} with problem name {problem_name}" + ) return evaluation_run_id - @db_operation async def create_evaluation_runs(conn: DatabaseConnection, evaluation_id: UUID, problem_names: List[str]) -> None: await conn.executemany( @@ -136,15 +139,16 @@ async def create_evaluation_runs(conn: DatabaseConnection, evaluation_id: UUID, created_at ) VALUES ($1, $2, $3, $4, NOW()) """, - [(uuid4(), evaluation_id, problem_name, EvaluationRunStatus.pending.value) for problem_name in problem_names] + [(uuid4(), evaluation_id, problem_name, EvaluationRunStatus.pending.value) for problem_name in problem_names], ) logger.debug(f"Created {len(problem_names)} evaluation runs for evaluation {evaluation_id}") - @db_operation -async def create_evaluation_run_log(conn: DatabaseConnection, evaluation_run_id: UUID, type: EvaluationRunLogType, logs: str) -> None: +async def create_evaluation_run_log( + conn: DatabaseConnection, evaluation_run_id: UUID, type: EvaluationRunLogType, logs: str +) -> None: await conn.execute( """ INSERT INTO evaluation_run_logs ( @@ -155,16 +159,19 @@ async def create_evaluation_run_log(conn: DatabaseConnection, evaluation_run_id: """, evaluation_run_id, type, - logs.replace('\x00', '') + logs.replace("\x00", ""), ) - num_lines = len(logs.split('\n')) - logger.debug(f"Created evaluation run log for evaluation run {evaluation_run_id} with type {type}, {num_lines} line(s), {len(logs)} character(s)") - + num_lines = len(logs.split("\n")) + logger.debug( + f"Created evaluation run log for evaluation run {evaluation_run_id} with type {type}, {num_lines} line(s), {len(logs)} character(s)" + ) @db_operation -async def check_if_evaluation_run_logs_exist(conn: DatabaseConnection, evaluation_run_id: UUID, type: EvaluationRunLogType) -> bool: +async def check_if_evaluation_run_logs_exist( + conn: DatabaseConnection, evaluation_run_id: UUID, type: EvaluationRunLogType +) -> bool: return await conn.fetchval( """ SELECT EXISTS ( @@ -173,13 +180,14 @@ async def check_if_evaluation_run_logs_exist(conn: DatabaseConnection, evaluatio ) """, evaluation_run_id, - type.value + type.value, ) - @db_operation -async def get_evaluation_run_logs_by_id(conn: DatabaseConnection, evaluation_run_id: UUID, type: EvaluationRunLogType) -> Optional[str]: +async def get_evaluation_run_logs_by_id( + conn: DatabaseConnection, evaluation_run_id: UUID, type: EvaluationRunLogType +) -> Optional[str]: logs = await conn.fetchval( """ SELECT logs FROM evaluation_run_logs @@ -187,7 +195,7 @@ async def get_evaluation_run_logs_by_id(conn: DatabaseConnection, evaluation_run and evaluation_run_id = $2 """, type, - evaluation_run_id + evaluation_run_id, ) - return logs \ No newline at end of file + return logs diff --git a/queries/evaluation_set.py b/queries/evaluation_set.py index bc3520277..51b3e7580 100644 --- a/queries/evaluation_set.py +++ b/queries/evaluation_set.py @@ -1,22 +1,24 @@ -from typing import List from datetime import datetime -from utils.database import db_operation, DatabaseConnection -from models.evaluation_set import EvaluationSetGroup, EvaluationSetProblem +from typing import List +from models.evaluation_set import EvaluationSetGroup, EvaluationSetProblem +from utils.database import DatabaseConnection, db_operation @db_operation async def get_latest_set_id(conn: DatabaseConnection) -> int: return await conn.fetchval("SELECT MAX(set_id) FROM evaluation_sets") + @db_operation async def get_set_created_at(conn: DatabaseConnection, set_id: int) -> datetime: return await conn.fetchval("SELECT MIN(created_at) FROM evaluation_sets WHERE set_id = $1", set_id) - @db_operation -async def get_all_problem_names_in_set_group_in_set_id(conn: DatabaseConnection, set_id: int, set_group: EvaluationSetGroup) -> List[str]: +async def get_all_problem_names_in_set_group_in_set_id( + conn: DatabaseConnection, set_id: int, set_group: EvaluationSetGroup +) -> List[str]: results = await conn.fetch( """ SELECT problem_name @@ -25,22 +27,23 @@ async def get_all_problem_names_in_set_group_in_set_id(conn: DatabaseConnection, ORDER BY problem_name """, set_id, - set_group.value + set_group.value, ) - - return [row["problem_name"] for row in results] + return [row["problem_name"] for row in results] @db_operation -async def get_all_evaluation_set_problems_for_set_id(conn: DatabaseConnection, set_id: int) -> List[EvaluationSetProblem]: +async def get_all_evaluation_set_problems_for_set_id( + conn: DatabaseConnection, set_id: int +) -> List[EvaluationSetProblem]: results = await conn.fetch( """ SELECT * FROM evaluation_sets WHERE set_id = $1 """, - set_id + set_id, ) return [EvaluationSetProblem(**result) for result in results] diff --git a/queries/inference.py b/queries/inference.py index 678fbdac2..90319c9ab 100644 --- a/queries/inference.py +++ b/queries/inference.py @@ -1,20 +1,18 @@ import json - -from uuid import UUID, uuid4 from typing import Any, List, Optional -from utils.database import db_operation, DatabaseConnection -from inference_gateway.models import InferenceMessage +from uuid import UUID, uuid4 +from inference_gateway.models import InferenceMessage +from utils.database import DatabaseConnection, db_operation def _remove_null_bytes(x: Any) -> Any: if isinstance(x, str): - return x.replace('\x00', '') + return x.replace("\x00", "") elif isinstance(x, dict): return {k: _remove_null_bytes(v) for k, v in x.items()} - - return x + return x @db_operation @@ -22,11 +20,10 @@ async def create_new_inference( conn: DatabaseConnection, *, evaluation_run_id: UUID, - provider: str, model: str, temperature: float, - messages: List[InferenceMessage] + messages: List[InferenceMessage], ) -> UUID: inference_id = uuid4() @@ -47,28 +44,25 @@ async def create_new_inference( """, inference_id, evaluation_run_id, - provider, model, temperature, - json.dumps([_remove_null_bytes(message.model_dump()) for message in messages]) + json.dumps([_remove_null_bytes(message.model_dump()) for message in messages]), ) return inference_id - @db_operation async def update_inference_by_id( conn: DatabaseConnection, *, inference_id: UUID, - status_code: Optional[int] = None, response: Optional[str] = None, num_input_tokens: Optional[int] = None, num_output_tokens: Optional[int] = None, - cost_usd: Optional[float] = None + cost_usd: Optional[float] = None, ) -> None: await conn.execute( """ @@ -88,14 +82,10 @@ async def update_inference_by_id( _remove_null_bytes(response), num_input_tokens, num_output_tokens, - cost_usd + cost_usd, ) - @db_operation async def get_number_of_inferences_for_evaluation_run(conn: DatabaseConnection, evaluation_run_id: UUID) -> int: - return await conn.fetchval( - """SELECT COUNT(*) FROM inferences WHERE evaluation_run_id = $1""", - evaluation_run_id - ) \ No newline at end of file + return await conn.fetchval("""SELECT COUNT(*) FROM inferences WHERE evaluation_run_id = $1""", evaluation_run_id) diff --git a/queries/payments.py b/queries/payments.py index c737362ae..ca295fbbf 100644 --- a/queries/payments.py +++ b/queries/payments.py @@ -2,20 +2,21 @@ from uuid import UUID from models.payments import Payment -from utils.database import db_operation, DatabaseConnection +from utils.database import DatabaseConnection, db_operation @db_operation async def record_evaluation_payment( conn: DatabaseConnection, payment_block_hash: str, - payment_extrinsic_index: str, + payment_extrinsic_index: str, amount_rao: int, agent_id: UUID, miner_hotkey: str, - miner_coldkey: str + miner_coldkey: str, ): - await conn.execute(""" + await conn.execute( + """ INSERT INTO evaluation_payments ( payment_block_hash, payment_extrinsic_index, @@ -27,7 +28,15 @@ async def record_evaluation_payment( amount_rao ) VALUES ($1, $2, $3, $4, $5, $6) - """, payment_block_hash, payment_extrinsic_index, str(agent_id), miner_hotkey, miner_coldkey, amount_rao) + """, + payment_block_hash, + payment_extrinsic_index, + str(agent_id), + miner_hotkey, + miner_coldkey, + amount_rao, + ) + @db_operation async def retrieve_payment_by_hash( @@ -35,15 +44,19 @@ async def retrieve_payment_by_hash( payment_block_hash: str, payment_extrinsic_index: str, ) -> Optional[Payment]: - result = await conn.fetchrow(""" + result = await conn.fetchrow( + """ select * from evaluation_payments where payment_block_hash = $1 and payment_extrinsic_index = $2 order by created_at desc limit 1 - """, payment_block_hash, payment_extrinsic_index) + """, + payment_block_hash, + payment_extrinsic_index, + ) if result is None: - return None - - return Payment(**result) \ No newline at end of file + return None + + return Payment(**result) diff --git a/queries/problem_statistics.py b/queries/problem_statistics.py index 405c50704..a8234d8ad 100644 --- a/queries/problem_statistics.py +++ b/queries/problem_statistics.py @@ -1,19 +1,19 @@ import json - -from uuid import UUID from datetime import datetime -from pydantic import BaseModel from typing import List, Optional -from models.problem import ProblemDifficulty -from models.evaluation_set import EvaluationSetGroup -from models.evaluation_run import EvaluationRunErrorCode -from utils.database import db_operation, DatabaseConnection +from uuid import UUID + +from pydantic import BaseModel + +from evaluator.problem_suites.polyglot.polyglot_suite import POLYGLOT_JS_SUITE, POLYGLOT_PY_SUITE from evaluator.problem_suites.problem_suite import ProblemSuiteName -from queries.evaluation_set import get_all_evaluation_set_problems_for_set_id -from evaluator.problem_suites.polyglot.polyglot_suite import POLYGLOT_PY_SUITE, POLYGLOT_JS_SUITE from evaluator.problem_suites.swebench_verified.swebench_verified_suite import SWEBENCH_VERIFIED_SUITE +from models.evaluation_run import EvaluationRunErrorCode +from models.evaluation_set import EvaluationSetGroup +from models.problem import ProblemDifficulty +from queries.evaluation_set import get_all_evaluation_set_problems_for_set_id +from utils.database import DatabaseConnection, db_operation - class ProblemStatisticsTestInfo(BaseModel): name: str @@ -25,6 +25,7 @@ class ProblemStatisticsTestInfo(BaseModel): pass_rate: float + class ProblemStatisticsRecentErroredAgentInfo(BaseModel): agent_id: UUID name: str @@ -32,12 +33,13 @@ class ProblemStatisticsRecentErroredAgentInfo(BaseModel): evaluation_id: UUID evaluation_validator_hotkey: str - + evaluation_run_id: UUID evaluation_run_started_at: datetime evaluation_run_errored_at: datetime evaluation_run_error_message: Optional[str] = None + class ProblemStatisticsErrorCodeInfo(BaseModel): error_code: int error_message: str @@ -49,12 +51,14 @@ def __init__(self, **data): data["error_message"] = EvaluationRunErrorCode(data["error_code"]).get_error_message() super().__init__(**data) + class ProblemStatisticsTokenInfo(BaseModel): model: str num_input_tokens: int num_output_tokens: int + class ProblemStatisticsFastestAgentInfo(BaseModel): agent_id: UUID name: str @@ -67,7 +71,6 @@ class ProblemStatisticsFastestAgentInfo(BaseModel): evaluation_run_finished_at: datetime - class ProblemStatistics(BaseModel): problem_name: str problem_suite_name: Optional[ProblemSuiteName] = None @@ -105,18 +108,23 @@ def __init__(self, **data): data["tests"] = [ProblemStatisticsTestInfo(**item) for item in json.loads(data["tests"])] if "error_code_distribution" in data: - data["error_code_distribution"] = [ProblemStatisticsErrorCodeInfo(**item) for item in json.loads(data["error_code_distribution"])] - + data["error_code_distribution"] = [ + ProblemStatisticsErrorCodeInfo(**item) for item in json.loads(data["error_code_distribution"]) + ] + if "token_distribution" in data: - data["token_distribution"] = [ProblemStatisticsTokenInfo(**item) for item in json.loads(data["token_distribution"])] + data["token_distribution"] = [ + ProblemStatisticsTokenInfo(**item) for item in json.loads(data["token_distribution"]) + ] if "fastest_agents" in data: - data["fastest_agents"] = [ProblemStatisticsFastestAgentInfo(**item) for item in json.loads(data["fastest_agents"])] + data["fastest_agents"] = [ + ProblemStatisticsFastestAgentInfo(**item) for item in json.loads(data["fastest_agents"]) + ] super().__init__(**data) - @db_operation async def get_problem_statistics(conn: DatabaseConnection, set_id: int) -> List[ProblemStatistics]: rows = await conn.fetch( @@ -333,7 +341,7 @@ async def get_problem_statistics(conn: DatabaseConnection, set_id: int) -> List[ LEFT JOIN token_distribution_stats tdss ON s.problem_name = tdss.problem_name LEFT JOIN fastest_agents_stats fas ON s.problem_name = fas.problem_name """, - set_id + set_id, ) problem_stats = [ProblemStatistics(**row) for row in rows] @@ -341,12 +349,25 @@ async def get_problem_statistics(conn: DatabaseConnection, set_id: int) -> List[ evaluation_set_problems = await get_all_evaluation_set_problems_for_set_id(set_id) for evaluation_set_problem in evaluation_set_problems: if not any(problem_stat.problem_name == evaluation_set_problem.problem_name for problem_stat in problem_stats): - problem_stats.append(ProblemStatistics( - problem_name=evaluation_set_problem.problem_name, - - in_screener_1_set_group=any(_evaluation_set_problem.problem_name == evaluation_set_problem.problem_name and _evaluation_set_problem.set_group == EvaluationSetGroup.screener_1 for _evaluation_set_problem in evaluation_set_problems), - in_screener_2_set_group=any(_evaluation_set_problem.problem_name == evaluation_set_problem.problem_name and _evaluation_set_problem.set_group == EvaluationSetGroup.screener_2 for _evaluation_set_problem in evaluation_set_problems), - in_validator_set_group=any(_evaluation_set_problem.problem_name == evaluation_set_problem.problem_name and _evaluation_set_problem.set_group == EvaluationSetGroup.validator for _evaluation_set_problem in evaluation_set_problems) - )) + problem_stats.append( + ProblemStatistics( + problem_name=evaluation_set_problem.problem_name, + in_screener_1_set_group=any( + _evaluation_set_problem.problem_name == evaluation_set_problem.problem_name + and _evaluation_set_problem.set_group == EvaluationSetGroup.screener_1 + for _evaluation_set_problem in evaluation_set_problems + ), + in_screener_2_set_group=any( + _evaluation_set_problem.problem_name == evaluation_set_problem.problem_name + and _evaluation_set_problem.set_group == EvaluationSetGroup.screener_2 + for _evaluation_set_problem in evaluation_set_problems + ), + in_validator_set_group=any( + _evaluation_set_problem.problem_name == evaluation_set_problem.problem_name + and _evaluation_set_problem.set_group == EvaluationSetGroup.validator + for _evaluation_set_problem in evaluation_set_problems + ), + ) + ) return problem_stats diff --git a/queries/scores.py b/queries/scores.py index 0cfc8a473..404956dad 100644 --- a/queries/scores.py +++ b/queries/scores.py @@ -1,6 +1,6 @@ -from typing import Optional, Dict +from typing import Dict, Optional -from utils.database import db_operation, DatabaseConnection +from utils.database import DatabaseConnection, db_operation @db_operation @@ -24,6 +24,7 @@ async def get_weight_receiving_agent_hotkey(conn: DatabaseConnection) -> Optiona return None return current_leader["miner_hotkey"] + @db_operation async def get_weight_receiving_agent_info(conn: DatabaseConnection) -> Optional[Dict[str, str]]: current_leader = await conn.fetchrow( @@ -43,4 +44,4 @@ async def get_weight_receiving_agent_info(conn: DatabaseConnection) -> Optional[ ) if current_leader is None or "miner_hotkey" not in current_leader or "agent_id" not in current_leader: return None - return current_leader \ No newline at end of file + return current_leader diff --git a/queries/statistics.py b/queries/statistics.py index ec5b2e2a9..5fe970eee 100644 --- a/queries/statistics.py +++ b/queries/statistics.py @@ -1,11 +1,11 @@ -import api.config as config - from datetime import datetime -from pydantic import BaseModel from typing import Dict, Optional -from models.evaluation_set import EvaluationSetGroup -from utils.database import db_operation, DatabaseConnection +from pydantic import BaseModel + +import api.config as config +from models.evaluation_set import EvaluationSetGroup +from utils.database import DatabaseConnection, db_operation @db_operation @@ -16,6 +16,7 @@ async def top_score(conn: DatabaseConnection) -> Optional[float]: AND agent_id NOT IN (SELECT agent_id FROM benchmark_agent_ids) """) + @db_operation async def agents_created_24_hrs(conn: DatabaseConnection) -> int: return await conn.fetchval(""" @@ -26,6 +27,7 @@ async def agents_created_24_hrs(conn: DatabaseConnection) -> int: AND agent_id NOT IN (SELECT agent_id FROM benchmark_agent_ids) """) + @db_operation async def score_improvement_24_hrs(conn: DatabaseConnection) -> float: return await conn.fetchval( @@ -44,10 +46,12 @@ async def score_improvement_24_hrs(conn: DatabaseConnection) -> float: """ ) + class TopScoreOverTime(BaseModel): hour: datetime top_score: float + @db_operation async def get_top_scores_over_time(conn: DatabaseConnection) -> list[TopScoreOverTime]: query = """ @@ -105,13 +109,13 @@ async def get_top_scores_over_time(conn: DatabaseConnection) -> list[TopScoreOve return [TopScoreOverTime(**row) for row in rows] - class PerfectlySolvedOverTime(BaseModel): hour: datetime polyglot_py: int polyglot_js: int swebench: int + @db_operation async def get_perfectly_solved_over_time(conn: DatabaseConnection) -> list[PerfectlySolvedOverTime]: query = """ @@ -156,11 +160,12 @@ async def get_perfectly_solved_over_time(conn: DatabaseConnection) -> list[Perfe return [PerfectlySolvedOverTime(**row) for row in rows] - # NOTE: None is returned if there are no successful evaluations for a given -# evaluation set group. +# evaluation set group. @db_operation -async def get_average_score_per_evaluation_set_group(conn: DatabaseConnection) -> Dict[EvaluationSetGroup, Optional[float]]: +async def get_average_score_per_evaluation_set_group( + conn: DatabaseConnection, +) -> Dict[EvaluationSetGroup, Optional[float]]: rows = await conn.fetch( """ SELECT @@ -189,11 +194,12 @@ async def get_average_score_per_evaluation_set_group(conn: DatabaseConnection) - return result - # NOTE: None is returned if there are no successful evaluations for a given -# evaluation set group. +# evaluation set group. @db_operation -async def get_average_wait_time_per_evaluation_set_group(conn: DatabaseConnection) -> Dict[EvaluationSetGroup, Optional[float]]: +async def get_average_wait_time_per_evaluation_set_group( + conn: DatabaseConnection, +) -> Dict[EvaluationSetGroup, Optional[float]]: result = {} result[EvaluationSetGroup.screener_1] = await conn.fetchval( @@ -262,11 +268,11 @@ async def get_average_wait_time_per_evaluation_set_group(conn: DatabaseConnectio return result - class ProblemSetCreationTime(BaseModel): set_id: int created_at: datetime + @db_operation async def get_problem_set_creation_times(conn: DatabaseConnection) -> list[ProblemSetCreationTime]: rows = await conn.fetch( @@ -276,5 +282,5 @@ async def get_problem_set_creation_times(conn: DatabaseConnection) -> list[Probl WHERE set_id >= 6 ORDER BY set_id ASC """ ) - + return [ProblemSetCreationTime(**row) for row in rows] diff --git a/ridges.py b/ridges.py index 3e4cf37a8..f05b4c5c2 100755 --- a/ridges.py +++ b/ridges.py @@ -5,24 +5,26 @@ """ import hashlib -from bittensor_wallet.wallet import Wallet -import httpx import os import subprocess +import time from typing import Optional + +import httpx +from bittensor import Subtensor +from bittensor_wallet.wallet import Wallet from dotenv import load_dotenv from rich.console import Console from rich.panel import Panel from rich.progress import Progress, SpinnerColumn, TextColumn from rich.prompt import Prompt -from bittensor import Subtensor -import time console = Console() DEFAULT_API_BASE_URL = "https://agent-upload.ridges.ai" load_dotenv(".env") + def run_cmd(cmd: str, capture: bool = True) -> tuple[int, str, str]: """Run command and return (code, stdout, stderr)""" try: @@ -48,8 +50,17 @@ def run_cmd(cmd: str, capture: bool = True) -> tuple[int, str, str]: # Forward KeyboardInterrupt to subprocess by killing it # This ensures proper cleanup when user presses Ctrl+C raise -run_cmd("uv add click") -import click + + +try: + import click +except ImportError: + print("Installing click...") + run_cmd("uv add click") + import click + + print("Click installed successfully") + def get_or_prompt(key: str, prompt: str, default: Optional[str] = None) -> str: """Get value from environment or prompt user.""" @@ -58,10 +69,11 @@ def get_or_prompt(key: str, prompt: str, default: Optional[str] = None) -> str: value = Prompt.ask(f"🎯 {prompt}", default=default) if default else Prompt.ask(f"🎯 {prompt}") return value + class RidgesCLI: def __init__(self, api_url: Optional[str] = None): self.api_url = api_url or DEFAULT_API_BASE_URL - + @click.group() @click.version_option(version="1.0.0") @@ -70,7 +82,8 @@ def __init__(self, api_url: Optional[str] = None): def cli(ctx, url): """Ridges CLI - Manage your Ridges miners and validators""" ctx.ensure_object(dict) - ctx.obj['url'] = url + ctx.obj["url"] = url + @cli.command() @click.option("--file", help="Path to agent.py file") @@ -79,8 +92,8 @@ def cli(ctx, url): @click.pass_context def upload(ctx, file: Optional[str], coldkey_name: Optional[str], hotkey_name: Optional[str]): """Upload a miner agent to the Ridges API.""" - ridges = RidgesCLI(ctx.obj.get('url')) - + ridges = RidgesCLI(ctx.obj.get("url")) + coldkey = coldkey_name or get_or_prompt("RIDGES_COLDKEY_NAME", "Enter your coldkey name", "miner") hotkey = hotkey_name or get_or_prompt("RIDGES_HOTKEY_NAME", "Enter your hotkey name", "default") wallet = Wallet(name=coldkey, hotkey=hotkey) @@ -89,19 +102,27 @@ def upload(ctx, file: Optional[str], coldkey_name: Optional[str], hotkey_name: O if not os.path.exists(file) or os.path.basename(file) != "agent.py": console.print("File must be named 'agent.py' and exist", style="bold red") return - - console.print(Panel(f"[bold cyan]Uploading Agent[/bold cyan]\n[yellow]Hotkey:[/yellow] {wallet.hotkey.ss58_address}\n[yellow]File:[/yellow] {file}\n[yellow]API:[/yellow] {ridges.api_url}", title="Upload", border_style="cyan")) - + + console.print( + Panel( + f"[bold cyan]Uploading Agent[/bold cyan]\n[yellow]Hotkey:[/yellow] {wallet.hotkey.ss58_address}\n[yellow]File:[/yellow] {file}\n[yellow]API:[/yellow] {ridges.api_url}", + title="Upload", + border_style="cyan", + ) + ) + try: - with open(file, 'rb') as f: + with open(file, "rb") as f: file_content = f.read() - + content_hash = hashlib.sha256(file_content).hexdigest() public_key = wallet.hotkey.public_key.hex() - + with httpx.Client() as client: - response = client.get(f"{ridges.api_url}/retrieval/agent-by-hotkey?miner_hotkey={wallet.hotkey.ss58_address}") - + response = client.get( + f"{ridges.api_url}/retrieval/agent-by-hotkey?miner_hotkey={wallet.hotkey.ss58_address}" + ) + if response.status_code == 200 and response.json(): latest_agent = response.json() name = latest_agent.get("name") @@ -110,16 +131,21 @@ def upload(ctx, file: Optional[str], coldkey_name: Optional[str], hotkey_name: O name = Prompt.ask("Enter a name for your miner agent") version_num = 0 - # Check if agent can be uploaded + # Check if agent can be uploaded check_file_info = f"{wallet.hotkey.ss58_address}:{content_hash}:{version_num}" check_payload = { - 'public_key': public_key, - 'file_info': check_file_info, - 'signature': wallet.hotkey.sign(check_file_info).hex(), - 'name': name, - 'payment_time': time.time() + "public_key": public_key, + "file_info": check_file_info, + "signature": wallet.hotkey.sign(check_file_info).hex(), + "name": name, + "payment_time": time.time(), } - check_response = client.post(f"{ridges.api_url}/upload/agent/check", files={'agent_file': ('agent.py', file_content, 'text/plain')}, data=check_payload, timeout=120) + check_response = client.post( + f"{ridges.api_url}/upload/agent/check", + files={"agent_file": ("agent.py", file_content, "text/plain")}, + data=check_payload, + timeout=120, + ) if check_response.status_code != 200: console.print(f"Error checking agent: {check_response.text}", style="bold red") return @@ -131,61 +157,77 @@ def upload(ctx, file: Optional[str], coldkey_name: Optional[str], hotkey_name: O if payment_response.status_code != 200: console.print("Error fetching evaluation cost", style="bold red") return - + payment_method_details = payment_response.json() - + confirm_payment = Prompt.ask( - f"\n[bold yellow]Proceed with payment of {payment_method_details['amount_rao']} RAO ({payment_method_details['amount_rao'] / 1e9} TAO) to {payment_method_details['send_address']}?[/bold yellow]", - choices=["y", "n"], - default="n" + f"\n[bold yellow]Proceed with payment of {payment_method_details['amount_rao']} RAO ({payment_method_details['amount_rao'] / 1e9} TAO) to {payment_method_details['send_address']}?[/bold yellow]", + choices=["y", "n"], + default="n", ) if confirm_payment.lower() != "y": console.print("[bold red]Payment cancelled by user. Upload aborted.[/bold red]") return - subtensor = Subtensor(network=os.environ.get('SUBTENSOR_NETWORK', 'finney')) + subtensor = Subtensor(network=os.environ.get("SUBTENSOR_NETWORK", "finney")) # Transfer payment_payload = subtensor.substrate.compose_call( call_module="Balances", call_function="transfer_keep_alive", call_params={ - 'dest': payment_method_details['send_address'], - 'value': payment_method_details['amount_rao'], - } + "dest": payment_method_details["send_address"], + "value": payment_method_details["amount_rao"], + }, ) - payment_extrinsic = subtensor.substrate.create_signed_extrinsic(call=payment_payload, keypair=wallet.coldkey) + payment_extrinsic = subtensor.substrate.create_signed_extrinsic( + call=payment_payload, keypair=wallet.coldkey + ) receipt = subtensor.substrate.submit_extrinsic(payment_extrinsic, wait_for_finalization=True) file_info = f"{wallet.hotkey.ss58_address}:{content_hash}:{version_num}" signature = wallet.hotkey.sign(file_info).hex() payload = { - 'public_key': public_key, - 'file_info': file_info, - 'signature': signature, - 'name': name, - 'payment_block_hash': receipt.block_hash, - 'payment_extrinsic_index': receipt.extrinsic_idx, - 'payment_time': payment_time_start + "public_key": public_key, + "file_info": file_info, + "signature": signature, + "name": name, + "payment_block_hash": receipt.block_hash, + "payment_extrinsic_index": receipt.extrinsic_idx, + "payment_time": payment_time_start, } - console.print(f"\n[yellow]Payment extrinsic submitted. If something goes wrong with the upload, you can use this information to get a refund[/yellow]") + console.print( + "\n[yellow]Payment extrinsic submitted. If something goes wrong with the upload, you can use this information to get a refund[/yellow]" + ) console.print(f"[cyan]Payment Block Hash:[/cyan] {receipt.block_hash}") console.print(f"[cyan]Payment Extrinsic Index:[/cyan] {receipt.extrinsic_idx}\n") - files = {'agent_file': ('agent.py', file_content, 'text/plain')} + files = {"agent_file": ("agent.py", file_content, "text/plain")} - with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console, transient=True) as progress: + with Progress( + SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console, transient=True + ) as progress: progress.add_task("Signing and uploading...", total=None) response = client.post(f"{ridges.api_url}/upload/agent", files=files, data=payload, timeout=120) - + if response.status_code == 200: - console.print(Panel(f"[bold green]Upload Complete[/bold green]\n[cyan]Miner '{name}' uploaded successfully![/cyan]", title="Success", border_style="green")) + console.print( + Panel( + f"[bold green]Upload Complete[/bold green]\n[cyan]Miner '{name}' uploaded successfully![/cyan]", + title="Success", + border_style="green", + ) + ) else: - error = response.json().get('detail', 'Unknown error') if response.headers.get('content-type', '').startswith('application/json') else response.text + error = ( + response.json().get("detail", "Unknown error") + if response.headers.get("content-type", "").startswith("application/json") + else response.text + ) console.print(f"Upload failed: {error}", style="bold red") - + except Exception as e: console.print(f"Error: {e}", style="bold red") raise e @@ -193,4 +235,4 @@ def upload(ctx, file: Optional[str], coldkey_name: Optional[str], hotkey_name: O if __name__ == "__main__": run_cmd(". .venv/bin/activate") - cli() \ No newline at end of file + cli() diff --git a/test_agent.py b/test_agent.py index 244d754f3..cfd62be11 100644 --- a/test_agent.py +++ b/test_agent.py @@ -1,32 +1,34 @@ -import os -import json -import click import asyncio +import json +import os import pathlib import traceback -import utils.logger as logger - -from uuid import uuid4 from datetime import datetime from typing import List, Optional +from uuid import uuid4 + +import click + +import utils.logger as logger from evaluator.models import EvaluationRunException -from evaluator.sandbox.sandbox_manager import SandboxManager +from evaluator.problem_suites.polyglot.polyglot_suite import ( + POLYGLOT_JS_SUITE, + POLYGLOT_JS_UNPATCHED_SUITE, + POLYGLOT_PY_SUITE, + POLYGLOT_PY_UNPATCHED_SUITE, +) from evaluator.problem_suites.problem_suite import ProblemSuite -from models.problem import ProblemDifficulty, ProblemTestResultStatus -from models.evaluation_run import EvaluationRun, EvaluationRunStatus, EvaluationRunErrorCode from evaluator.problem_suites.swebench_verified.swebench_verified_suite import SWEBENCH_VERIFIED_SUITE -from evaluator.problem_suites.polyglot.polyglot_suite import POLYGLOT_JS_SUITE, POLYGLOT_PY_SUITE, POLYGLOT_JS_UNPATCHED_SUITE, POLYGLOT_PY_UNPATCHED_SUITE - - +from evaluator.sandbox.sandbox_manager import SandboxManager +from models.evaluation_run import EvaluationRun, EvaluationRunErrorCode, EvaluationRunStatus +from models.problem import ProblemDifficulty, ProblemTestResultStatus TEST_AGENT_RESULTS_DIR = pathlib.Path(__file__).parent / "test_agent_results" - evaluation_id = uuid4() - # Options global inference_gateway_url global agent_path @@ -40,29 +42,24 @@ global problem_suites - class LocalEvaluationRun(EvaluationRun): agent_logs: Optional[str] = None eval_logs: Optional[str] = None - -async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suites: List[ProblemSuite], problem_name: str): +async def run_local_evaluation_run( + sandbox_manager: SandboxManager, problem_suites: List[ProblemSuite], problem_name: str +): evaluation_run = LocalEvaluationRun( evaluation_run_id=uuid4(), evaluation_id=evaluation_id, - problem_name=problem_name, - status=EvaluationRunStatus.pending, - patch=None, test_results=None, - error_code=None, error_message=None, - - created_at=datetime.now() + created_at=datetime.now(), ) try: @@ -72,8 +69,7 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit logger.error(f"[{problem_name}] The problem '{problem_name}' was not found") raise EvaluationRunException( - EvaluationRunErrorCode.VALIDATOR_UNKNOWN_PROBLEM, - f"The problem '{problem_name}' was not found" + EvaluationRunErrorCode.VALIDATOR_UNKNOWN_PROBLEM, f"The problem '{problem_name}' was not found" ) problem = problem_suite.get_problem(problem_name) @@ -84,13 +80,13 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit logger.info(f"[{problem_name}] Initializing agent...") agent_sandbox = await asyncio.to_thread( problem_suite.initialize_agent_sandbox, - sandbox_manager, - problem, + sandbox_manager, + problem, evaluation_run.evaluation_run_id, agent_code, running_agent_timeout_seconds, include_solutions=include_solutions, - include_tests=include_tests + include_tests=include_tests, ) logger.info(f"[{problem_name}] Finished initializing agent") @@ -98,12 +94,10 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit evaluation_run.started_running_agent_at = datetime.now() logger.info(f"[{problem_name}] Running agent...") - patch, agent_logs = await asyncio.to_thread( - problem_suite.run_agent_sandbox, - sandbox_manager, - agent_sandbox + patch, agent_logs = await asyncio.to_thread(problem_suite.run_agent_sandbox, sandbox_manager, agent_sandbox) + logger.info( + f"[{problem_name}] Finished running agent: {len(patch.splitlines())} line(s) of patch, {len(agent_logs.splitlines())} line(s) of agent logs" ) - logger.info(f"[{problem_name}] Finished running agent: {len(patch.splitlines())} line(s) of patch, {len(agent_logs.splitlines())} line(s) of agent logs") evaluation_run.patch = patch evaluation_run.agent_logs = agent_logs @@ -118,7 +112,7 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit problem, evaluation_run.evaluation_run_id, patch, - running_eval_timeout_seconds + running_eval_timeout_seconds, ) logger.info(f"[{problem_name}] Finished initializing evaluation") @@ -126,18 +120,18 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit evaluation_run.started_running_eval_at = datetime.now() logger.info(f"[{problem_name}] Running evaluation...") - test_results, eval_logs = await asyncio.to_thread( - problem_suite.run_eval_sandbox, - sandbox_manager, - eval_sandbox - ) + test_results, eval_logs = await asyncio.to_thread(problem_suite.run_eval_sandbox, sandbox_manager, eval_sandbox) num_passed = sum(1 for test in test_results if test.status == ProblemTestResultStatus.PASS) num_failed = sum(1 for test in test_results if test.status == ProblemTestResultStatus.FAIL) num_skipped = sum(1 for test in test_results if test.status == ProblemTestResultStatus.SKIP) if num_failed > 0: - logger.error(f"[{problem_name}] Finished running evaluation: {num_passed} passed, {num_failed} failed, {num_skipped} skipped, {len(eval_logs.splitlines())} line(s) of eval logs") + logger.error( + f"[{problem_name}] Finished running evaluation: {num_passed} passed, {num_failed} failed, {num_skipped} skipped, {len(eval_logs.splitlines())} line(s) of eval logs" + ) else: - logger.info(f"[{problem_name}] Finished running evaluation: {num_passed} passed, {num_failed} failed, {num_skipped} skipped, {len(eval_logs.splitlines())} line(s) of eval logs") + logger.info( + f"[{problem_name}] Finished running evaluation: {num_passed} passed, {num_failed} failed, {num_skipped} skipped, {len(eval_logs.splitlines())} line(s) of eval logs" + ) evaluation_run.test_results = test_results evaluation_run.eval_logs = eval_logs @@ -163,13 +157,16 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit logger.error(f"[{problem_name}] Errored: {str(e)}") - - - test_agent_result_evaluation_dir = TEST_AGENT_RESULTS_DIR / f"{datetime.now().strftime('%Y-%m-%d')}__{pathlib.Path(agent_path).name}__{evaluation_id}" - test_agent_result_evaluation_run_dir = test_agent_result_evaluation_dir / f"{problem_name}__{evaluation_run.evaluation_run_id}" + test_agent_result_evaluation_dir = ( + TEST_AGENT_RESULTS_DIR + / f"{datetime.now().strftime('%Y-%m-%d')}__{pathlib.Path(agent_path).name}__{evaluation_id}" + ) + test_agent_result_evaluation_run_dir = ( + test_agent_result_evaluation_dir / f"{problem_name}__{evaluation_run.evaluation_run_id}" + ) logger.info(f"[{problem_name}] Saving results to {test_agent_result_evaluation_run_dir}...") - + os.makedirs(test_agent_result_evaluation_run_dir, exist_ok=True) with open(test_agent_result_evaluation_run_dir / "evaluation_run.json", "w") as f: @@ -185,26 +182,22 @@ async def run_local_evaluation_run(sandbox_manager: SandboxManager, problem_suit logger.info(f"[{problem_name}] Saved results to {test_agent_result_evaluation_run_dir}...") - - return evaluation_run - async def run_problems(agent_code: str, problem_names: List[str]): os.makedirs(TEST_AGENT_RESULTS_DIR, exist_ok=True) - - - test_agent_result_evaluation_dir = TEST_AGENT_RESULTS_DIR / f"{datetime.now().strftime('%Y-%m-%d')}__{pathlib.Path(agent_path).name}__{evaluation_id}" + test_agent_result_evaluation_dir = ( + TEST_AGENT_RESULTS_DIR + / f"{datetime.now().strftime('%Y-%m-%d')}__{pathlib.Path(agent_path).name}__{evaluation_id}" + ) os.makedirs(test_agent_result_evaluation_dir, exist_ok=True) with open(test_agent_result_evaluation_dir / pathlib.Path(agent_path).name, "w") as f: f.write(agent_code) - - sandbox_manager = SandboxManager(inference_gateway_url) SWEBENCH_VERIFIED_SUITE.prebuild_problem_images(problem_names) @@ -217,16 +210,43 @@ async def run_problems(agent_code: str, problem_names: List[str]): await asyncio.gather(*tasks) - @click.group() -@click.option("--inference-url", required=True, type=str, help="The inference gateway URL (e.g., http://192.168.0.1:1234)") -@click.option("--agent-path", "_agent_path", required=True, type=str, help="The path to the agent file (e.g., ~/agents/agent.py)") -@click.option("--agent-timeout", default=2400, type=int, help="The timeout in seconds for running the agent, in seconds") -@click.option("--eval-timeout", default=600, type=int, help="The timeout in seconds for running the evaluation, in seconds") -@click.option("--include-solutions", "_include_solutions", is_flag=True, help="Whether or not to include solutions in the evaluation") -@click.option("--include-tests", "_include_tests", is_flag=True, help="Whether or not to include tests in the evaluation") -@click.option("--polyglot-unpatched", "polyglot_unpatched", is_flag=True, help="Whether or not to use the unpatched Polyglot suite") -def cli(inference_url: str, _agent_path: str, agent_timeout: int, eval_timeout: int, _include_solutions: bool, _include_tests: bool, polyglot_unpatched: bool): +@click.option( + "--inference-url", required=True, type=str, help="The inference gateway URL (e.g., http://192.168.0.1:1234)" +) +@click.option( + "--agent-path", "_agent_path", required=True, type=str, help="The path to the agent file (e.g., ~/agents/agent.py)" +) +@click.option( + "--agent-timeout", default=2400, type=int, help="The timeout in seconds for running the agent, in seconds" +) +@click.option( + "--eval-timeout", default=600, type=int, help="The timeout in seconds for running the evaluation, in seconds" +) +@click.option( + "--include-solutions", + "_include_solutions", + is_flag=True, + help="Whether or not to include solutions in the evaluation", +) +@click.option( + "--include-tests", "_include_tests", is_flag=True, help="Whether or not to include tests in the evaluation" +) +@click.option( + "--polyglot-unpatched", + "polyglot_unpatched", + is_flag=True, + help="Whether or not to use the unpatched Polyglot suite", +) +def cli( + inference_url: str, + _agent_path: str, + agent_timeout: int, + eval_timeout: int, + _include_solutions: bool, + _include_tests: bool, + polyglot_unpatched: bool, +): global inference_gateway_url global agent_path global agent_code @@ -259,7 +279,6 @@ def cli(inference_url: str, _agent_path: str, agent_timeout: int, eval_timeout: problem_suites = [SWEBENCH_VERIFIED_SUITE, POLYGLOT_PY_SUITE, POLYGLOT_JS_SUITE] - @cli.command() @click.argument("problem_name", required=True, type=str) @click.option("--num-runs", default=1, type=int, help="The number of times to run the problem") @@ -267,7 +286,6 @@ def test_problem(problem_name: str, num_runs: int): asyncio.run(run_problems(agent_code, [problem_name] * num_runs)) - with open(pathlib.Path(__file__).parent / "test_agent_problem_sets.json", "r") as f: problem_sets = json.load(f) @@ -277,11 +295,26 @@ def test_problem(problem_name: str, num_runs: int): # Register all-swebench-verified, all-swebench-verified-easy, all-swebench-verified-medium, all-swebench-verified-hard, all-swebench-verified-impossible problem_sets["all-swebench-verified"] = [problem.name for problem in SWEBENCH_VERIFIED_SUITE.problems.values()] -problem_sets["all-swebench-verified-easy"] = [problem.name for problem in SWEBENCH_VERIFIED_SUITE.problems.values() if problem.difficulty == ProblemDifficulty.EASY] -problem_sets["all-swebench-verified-medium"] = [problem.name for problem in SWEBENCH_VERIFIED_SUITE.problems.values() if problem.difficulty == ProblemDifficulty.MEDIUM] -problem_sets["all-swebench-verified-hard"] = [problem.name for problem in SWEBENCH_VERIFIED_SUITE.problems.values() if problem.difficulty == ProblemDifficulty.HARD] -problem_sets["all-swebench-verified-impossible"] = [problem.name for problem in SWEBENCH_VERIFIED_SUITE.problems.values() if problem.difficulty == ProblemDifficulty.IMPOSSIBLE] - +problem_sets["all-swebench-verified-easy"] = [ + problem.name + for problem in SWEBENCH_VERIFIED_SUITE.problems.values() + if problem.difficulty == ProblemDifficulty.EASY +] +problem_sets["all-swebench-verified-medium"] = [ + problem.name + for problem in SWEBENCH_VERIFIED_SUITE.problems.values() + if problem.difficulty == ProblemDifficulty.MEDIUM +] +problem_sets["all-swebench-verified-hard"] = [ + problem.name + for problem in SWEBENCH_VERIFIED_SUITE.problems.values() + if problem.difficulty == ProblemDifficulty.HARD +] +problem_sets["all-swebench-verified-impossible"] = [ + problem.name + for problem in SWEBENCH_VERIFIED_SUITE.problems.values() + if problem.difficulty == ProblemDifficulty.IMPOSSIBLE +] @cli.command() @@ -290,35 +323,34 @@ def test_problem_set(problem_set_name: str): asyncio.run(run_problems(agent_code, problem_sets[problem_set_name])) - @cli.command() def list_problem_sets(): click.echo("\nAvailable Problem Sets:") click.echo("=" * 80) - + max_name_length = max(len(name) for name in problem_sets.keys()) for problem_set_name in sorted(problem_sets.keys()): num_problems = len(problem_sets[problem_set_name]) click.echo(f" {problem_set_name:<{max_name_length}} - {num_problems:>4} problem(s)") - + click.echo("=" * 80) click.echo(f"Total: {len(problem_sets)} problem set(s)\n") + @cli.command() @click.argument("problem_set_name", required=True, type=click.Choice(list(problem_sets.keys()))) def list_problems(problem_set_name: str): problems = problem_sets[problem_set_name] - + click.echo(f"\nProblems in '{problem_set_name}':") click.echo("=" * 80) - + for i, problem_name in enumerate(problems, 1): click.echo(f" {i:>3}. {problem_name}") - + click.echo("=" * 80) click.echo(f"Total: {len(problems)} problem(s)\n") - -if __name__ == '__main__': - cli() \ No newline at end of file +if __name__ == "__main__": + cli() diff --git a/utils/bittensor.py b/utils/bittensor.py index 860989094..676e803df 100644 --- a/utils/bittensor.py +++ b/utils/bittensor.py @@ -1,73 +1,74 @@ import asyncio -import utils.logger as logger import json import os from typing import Set -from bittensor_wallet.keypair import Keypair from bittensor.core.async_subtensor import AsyncSubtensor from bittensor_wallet.keypair import Keypair import api.config as config import utils.logger as logger - - subtensor = AsyncSubtensor(network=config.SUBTENSOR_NETWORK) -REGISTERED_HOTKEYS_FILE = os.path.join(os.path.dirname(__file__), 'registered_hotkeys.json') +REGISTERED_HOTKEYS_FILE = os.path.join(os.path.dirname(__file__), "registered_hotkeys.json") + async def fetch_and_save_registered_hotkeys() -> None: try: metagraph = await subtensor.metagraph(netuid=config.NETUID) registered_hotkeys = [neuron.hotkey for neuron in metagraph.neurons] - - with open(REGISTERED_HOTKEYS_FILE, 'w') as f: + + with open(REGISTERED_HOTKEYS_FILE, "w") as f: json.dump(registered_hotkeys, f, indent=2) - + logger.info(f"Successfully saved {len(registered_hotkeys)} registered hotkeys to {REGISTERED_HOTKEYS_FILE}") except Exception as e: logger.error(f"Error fetching and saving registered hotkeys: {e}") raise + def load_registered_hotkeys() -> Set[str]: try: if not os.path.exists(REGISTERED_HOTKEYS_FILE): logger.warning(f"Registered hotkeys file not found: {REGISTERED_HOTKEYS_FILE}") return set() - - with open(REGISTERED_HOTKEYS_FILE, 'r') as f: + + with open(REGISTERED_HOTKEYS_FILE, "r") as f: hotkeys_list = json.load(f) - + return set(hotkeys_list) except Exception as e: logger.error(f"Error loading registered hotkeys from {REGISTERED_HOTKEYS_FILE}: {e}") return set() + async def check_if_hotkey_is_registered(hotkey: str) -> bool: registered_hotkeys = load_registered_hotkeys() - + # If no hotkeys loaded from JSON, fall back to fetching from metagraph if not registered_hotkeys: logger.warning("No registered hotkeys found in JSON file, falling back to metagraph fetch") metagraph = await subtensor.metagraph(netuid=config.NETUID) registered_hotkeys = {neuron.hotkey for neuron in metagraph.neurons} - + return hotkey in registered_hotkeys + # async def check_if_hotkey_is_registered(hotkey: str) -> bool: # return await subtensor.is_hotkey_registered(hotkey_ss58=hotkey, netuid=config.NETUID) - def validate_signed_timestamp(timestamp: int, signed_timestamp: str, hotkey: str) -> bool: try: keypair = Keypair(ss58_address=hotkey) return keypair.verify(str(timestamp), bytes.fromhex(signed_timestamp)) except Exception as e: - logger.warning(f"Error in validate_signed_timestamp(timestamp={timestamp}, signed_timestamp={signed_timestamp}, hotkey={hotkey}): {e}") + logger.warning( + f"Error in validate_signed_timestamp(timestamp={timestamp}, signed_timestamp={signed_timestamp}, hotkey={hotkey}): {e}" + ) return False + if __name__ == "__main__": asyncio.run(fetch_and_save_registered_hotkeys()) - diff --git a/utils/coingecko.py b/utils/coingecko.py index 7e596c06b..fc60eb857 100644 --- a/utils/coingecko.py +++ b/utils/coingecko.py @@ -1,5 +1,6 @@ import httpx + async def get_tao_price() -> float: url = "https://api.coingecko.com/api/v3/simple/price" params = {"ids": "bittensor", "vs_currencies": "usd"} diff --git a/utils/database.py b/utils/database.py index 1221725d7..c1a9710af 100644 --- a/utils/database.py +++ b/utils/database.py @@ -1,67 +1,52 @@ # ADAM: Black magic file -import re -import time import asyncio -import asyncpg import contextvars -import utils.logger as logger - -from typing import Optional +import re +import time from functools import wraps +from typing import Optional from uuid import UUID, uuid4 +import asyncpg + +import utils.logger as logger async def initialize_database(*, username: str, password: str, host: str, port: int, name: str): logger.info(f"Connecting to database {name} as user {username} on {host}:{port}...") global pool - pool = await asyncpg.create_pool( - user=username, - password=password, - host=host, - port=port, - database=name - ) + pool = await asyncpg.create_pool(user=username, password=password, host=host, port=port, database=name) logger.info(f"Connected to database {name} as user {username} on {host}:{port}.") + async def deinitialize_database(): logger.info("Disconnecting from database...") - + global pool await pool.close() logger.info("Disconnected from database.") - -DEBUG_QUERIES = { - "running": [], - "slow": [] -} +DEBUG_QUERIES = {"running": [], "slow": []} DEBUG_QUERIES_LOCK = asyncio.Lock() - ACTIVE_CONNECTIONS = 0 ACTIVE_REUSED_CONNECTIONS = 0 - async def _begin_db_operation(label: str, query: str): id = uuid4() - running_entry = { - "id": id, - "label": label, - "query": re.sub(r'\s+', ' ', query).strip(), - "start_time": time.time() - } + running_entry = {"id": id, "label": label, "query": re.sub(r"\s+", " ", query).strip(), "start_time": time.time()} async with DEBUG_QUERIES_LOCK: DEBUG_QUERIES["running"].append(running_entry) return id + async def _end_db_operation(id: UUID): async with DEBUG_QUERIES_LOCK: running_entry = next(x for x in DEBUG_QUERIES["running"] if x["id"] == id) @@ -72,11 +57,10 @@ async def _end_db_operation(id: UUID): DEBUG_QUERIES["slow"].append(running_entry) - def get_debug_query_info(): now = time.time() - running_sorted = sorted(DEBUG_QUERIES["running"], key=lambda entry: now - entry["start_time"], reverse=True) + running_sorted = sorted(DEBUG_QUERIES["running"], key=lambda entry: now - entry["start_time"], reverse=True) running_info = [] for entry in running_sorted: seconds_running = now - entry["start_time"] @@ -92,11 +76,10 @@ def get_debug_query_info(): "active_connections": ACTIVE_CONNECTIONS, "active_reused_connections": ACTIVE_REUSED_CONNECTIONS, "running": running_info, - "slow": slow_info + "slow": slow_info, } - class DatabaseConnection: def __init__(self, conn: asyncpg.Connection, label: str): self.conn = conn @@ -138,8 +121,10 @@ async def fetchval(self, query: str, *args, **kwargs): await _end_db_operation(id) +_per_context_conn: contextvars.ContextVar[Optional[DatabaseConnection]] = contextvars.ContextVar( + "db_connection", default=None +) -_per_context_conn: contextvars.ContextVar[Optional[DatabaseConnection]] = contextvars.ContextVar('db_connection', default=None) def db_operation(func): @wraps(func) @@ -163,5 +148,5 @@ async def wrapper(*args, **kwargs): finally: _per_context_conn.reset(token) ACTIVE_CONNECTIONS -= 1 - - return wrapper \ No newline at end of file + + return wrapper diff --git a/utils/debug_lock.py b/utils/debug_lock.py index c352f3f9d..0db62d08a 100644 --- a/utils/debug_lock.py +++ b/utils/debug_lock.py @@ -1,20 +1,14 @@ # ADAM: Black magic file -import time import asyncio -import utils.logger as logger - +import time +import utils.logger as logger -DEBUG_LOCKS = { - "waiting": [], - "locked": [], - "slow": [] -} +DEBUG_LOCKS = {"waiting": [], "locked": [], "slow": []} DEBUG_LOCKS_LOCK = asyncio.Lock() - class DebugLock: def __init__(self, lock: asyncio.Lock, label: str, timeout: float = None): self.lock = lock @@ -24,10 +18,7 @@ def __init__(self, lock: asyncio.Lock, label: str, timeout: float = None): async def __aenter__(self): self.waiting_at = time.time() logger.info(f"[DebugLock] {self.label}: Trying to acquire lock...") - waiting_entry = { - "label": self.label, - "waiting_at": self.waiting_at - } + waiting_entry = {"label": self.label, "waiting_at": self.waiting_at} async with DEBUG_LOCKS_LOCK: DEBUG_LOCKS["waiting"].append(waiting_entry) try: @@ -37,17 +28,21 @@ async def __aenter__(self): await self.lock.acquire() except asyncio.TimeoutError: async with DEBUG_LOCKS_LOCK: - DEBUG_LOCKS["waiting"] = [x for x in DEBUG_LOCKS["waiting"] if not (x["label"] == self.label and x["waiting_at"] == self.waiting_at)] + DEBUG_LOCKS["waiting"] = [ + x + for x in DEBUG_LOCKS["waiting"] + if not (x["label"] == self.label and x["waiting_at"] == self.waiting_at) + ] logger.error(f"[DebugLock] {self.label}: Failed to acquire lock within {self.timeout} seconds") - raise + raise self.acquired_at = time.time() - acquired_entry = { - "label": self.label, - "waiting_at": self.waiting_at, - "acquired_at": self.acquired_at - } + acquired_entry = {"label": self.label, "waiting_at": self.waiting_at, "acquired_at": self.acquired_at} async with DEBUG_LOCKS_LOCK: - DEBUG_LOCKS["waiting"] = [x for x in DEBUG_LOCKS["waiting"] if not (x["label"] == self.label and x["waiting_at"] == self.waiting_at)] + DEBUG_LOCKS["waiting"] = [ + x + for x in DEBUG_LOCKS["waiting"] + if not (x["label"] == self.label and x["waiting_at"] == self.waiting_at) + ] DEBUG_LOCKS["locked"].append(acquired_entry) elapsed = self.acquired_at - self.waiting_at logger.info(f"[DebugLock] {self.label}: Lock acquired after waiting for {elapsed:.2f} seconds") @@ -61,36 +56,35 @@ async def __aexit__(self, exc_type, exc_value, traceback): "label": self.label, "waiting_at": self.waiting_at, "acquired_at": self.acquired_at, - "released_at": self.released_at + "released_at": self.released_at, } async with DEBUG_LOCKS_LOCK: - DEBUG_LOCKS["locked"] = [x for x in DEBUG_LOCKS["locked"] if not (x["label"] == self.label and x["waiting_at"] == self.waiting_at)] + DEBUG_LOCKS["locked"] = [ + x + for x in DEBUG_LOCKS["locked"] + if not (x["label"] == self.label and x["waiting_at"] == self.waiting_at) + ] if elapsed > 5: DEBUG_LOCKS["slow"].append(slow_entry) logger.info(f"[DebugLock] {self.label}: Lock released after being locked for {elapsed:.2f} seconds") - def get_debug_lock_info(): now = time.time() waiting_info = [] for entry in DEBUG_LOCKS["waiting"]: seconds_waiting = now - entry["waiting_at"] - waiting_info.append(f"{entry["label"]} - {seconds_waiting:.2f} s") + waiting_info.append(f"{entry['label']} - {seconds_waiting:.2f} s") locked_info = [] for entry in DEBUG_LOCKS["locked"]: seconds_locked = now - entry["acquired_at"] - locked_info.append(f"{entry["label"]} - {seconds_locked:.2f} s") + locked_info.append(f"{entry['label']} - {seconds_locked:.2f} s") slow_info = [] for entry in DEBUG_LOCKS["slow"]: seconds_locked = entry["released_at"] - entry["acquired_at"] - slow_info.append(f"{entry["label"]} - {seconds_locked:.2f} s") + slow_info.append(f"{entry['label']} - {seconds_locked:.2f} s") - return { - "waiting": waiting_info, - "locked": locked_info, - "slow": slow_info - } \ No newline at end of file + return {"waiting": waiting_info, "locked": locked_info, "slow": slow_info} diff --git a/utils/diff.py b/utils/diff.py index 216d92175..2601dce06 100644 --- a/utils/diff.py +++ b/utils/diff.py @@ -1,22 +1,21 @@ """Utilities for computing diffs between files.""" import os -import tempfile import subprocess -import utils.logger as logger - -from typing import Tuple, Optional +import tempfile +from typing import Optional, Tuple +import utils.logger as logger def get_file_diff(old_path, new_path) -> str: """ Gets the diff between two files. - + Args: old_path: The path to the old file new_path: The path to the new file - + Returns: The diff between the two files, expressed as a diff of the old file, as a string. """ @@ -28,13 +27,9 @@ def get_file_diff(old_path, new_path) -> str: missing.append(new_path) if missing: logger.fatal(f"File(s) not found for diff: {', '.join(missing)}") - + # Use diff command - result = subprocess.run( - ["diff", "-u", old_path, new_path], - capture_output=True, - text=True - ) + result = subprocess.run(["diff", "-u", old_path, new_path], capture_output=True, text=True) # Check if the diff was generated successfully # `diff -u` return codes: @@ -53,39 +48,33 @@ def get_file_diff(old_path, new_path) -> str: filename = os.path.basename(old_path) lines[0] = f"--- {filename}" lines[1] = f"+++ {filename}" - - return "\n".join(lines) + return "\n".join(lines) def validate_diff_for_local_repo(diff, local_repo_dir) -> Tuple[bool, Optional[str]]: """ Validates if a diff string is valid and can be applied to a local repository. - + Args: diff: The diff string to validate local_repo_dir: The local repository directory - + Returns: (is_valid: bool, error_message: Optional[str]) """ - + # Write diff to temp file with tempfile.NamedTemporaryFile(mode="w", suffix=".diff", delete=False) as f: f.write(diff) diff_file = f.name - + # Use `git apply --check` to validate without applying - result = subprocess.run( - ["git", "apply", "--check", diff_file], - cwd=local_repo_dir, - capture_output=True, - text=True - ) + result = subprocess.run(["git", "apply", "--check", diff_file], cwd=local_repo_dir, capture_output=True, text=True) # Delete the temp file os.unlink(diff_file) - + # Check if the diff was applied successfully if result.returncode == 0: return True, None @@ -93,11 +82,10 @@ def validate_diff_for_local_repo(diff, local_repo_dir) -> Tuple[bool, Optional[s return False, result.stderr.strip() - def apply_diff_to_local_repo(diff, local_repo_dir) -> None: """ Applies a diff string to files in the source directory. - + Args: diff: The diff string to apply local_repo_dir: The local repository directory @@ -107,18 +95,13 @@ def apply_diff_to_local_repo(diff, local_repo_dir) -> None: with tempfile.NamedTemporaryFile(mode="w", suffix=".diff", delete=False) as f: f.write(diff) diff_file = f.name - + # Use `git apply` to apply the diff - result = subprocess.run( - ["git", "apply", diff_file], - cwd=local_repo_dir, - capture_output=True, - text=True - ) + result = subprocess.run(["git", "apply", diff_file], cwd=local_repo_dir, capture_output=True, text=True) # Delete the temp file os.unlink(diff_file) # Check if the diff was applied successfully if result.returncode != 0: - logger.fatal(f"Failed to apply diff to {local_repo_dir}: {result.stderr.strip()}") \ No newline at end of file + logger.fatal(f"Failed to apply diff to {local_repo_dir}: {result.stderr.strip()}") diff --git a/utils/docker.py b/utils/docker.py index 735dadde9..7731fbe79 100644 --- a/utils/docker.py +++ b/utils/docker.py @@ -1,18 +1,16 @@ -import docker import subprocess -import utils.logger as logger - +import docker -DOCKER_PREFIX = 'ridges-ai' -SWEBENCH_DOCKER_PREFIX = 'sweb' +import utils.logger as logger +DOCKER_PREFIX = "ridges-ai" +SWEBENCH_DOCKER_PREFIX = "sweb" docker_client = None - def _initialize_docker(): logger.info("Initializing Docker...") try: @@ -23,13 +21,11 @@ def _initialize_docker(): logger.fatal(f"Failed to initialize Docker: {e}") - def get_docker_client(): if docker_client is None: _initialize_docker() - - return docker_client + return docker_client def build_docker_image(dockerfile_dir: str, tag: str) -> None: @@ -39,21 +35,21 @@ def build_docker_image(dockerfile_dir: str, tag: str) -> None: logger.info(f"Successfully built Docker image: {tag}") - def get_num_docker_containers() -> int: # This is equivalent to `docker ps -q | wc -l` result = subprocess.run(["docker", "ps", "-q"], capture_output=True, text=True, timeout=1) - return len([line for line in result.stdout.strip().split('\n') if line.strip()]) - + return len([line for line in result.stdout.strip().split("\n") if line.strip()]) # TODO ADAM: optimize def stop_and_delete_all_docker_containers() -> None: docker_client = get_docker_client() - - logger.info(f"Stopping and deleting all containers...") - - for container in docker_client.containers.list(all=True, filters={"name": f"^({DOCKER_PREFIX}|{SWEBENCH_DOCKER_PREFIX})"}): + + logger.info("Stopping and deleting all containers...") + + for container in docker_client.containers.list( + all=True, filters={"name": f"^({DOCKER_PREFIX}|{SWEBENCH_DOCKER_PREFIX})"} + ): logger.info(f"Stopping and deleting container {container.name}...") try: @@ -61,7 +57,7 @@ def stop_and_delete_all_docker_containers() -> None: except Exception as e: logger.warning(f"Failed to stop container {container.name}: {e}") # continue - + try: container.remove(force=True) except Exception as e: @@ -71,14 +67,13 @@ def stop_and_delete_all_docker_containers() -> None: logger.info(f"Stopped and deleted container {container.name}") docker_client.containers.prune() - - logger.info(f"Stopped and deleted all containers") + logger.info("Stopped and deleted all containers") def create_internal_docker_network(name: str) -> None: docker_client = get_docker_client() - + try: docker_client.networks.get(name) logger.info(f"Found internal Docker network: {name}") @@ -87,7 +82,6 @@ def create_internal_docker_network(name: str) -> None: logger.info(f"Created internal Docker network: {name}") - def connect_docker_container_to_internet(container: docker.models.containers.Container) -> None: docker_client = get_docker_client() @@ -95,5 +89,5 @@ def connect_docker_container_to_internet(container: docker.models.containers.Con bridge_network = docker_client.networks.get("bridge") bridge_network.connect(container) - - logger.info(f"Connected Docker container {container.name} to internet") \ No newline at end of file + + logger.info(f"Connected Docker container {container.name} to internet") diff --git a/utils/git.py b/utils/git.py index e9315db94..14076e8d1 100644 --- a/utils/git.py +++ b/utils/git.py @@ -3,96 +3,85 @@ import os import pathlib import subprocess -import utils.logger as logger +import utils.logger as logger def clone_repo(repo_url: str, target_dir: str) -> None: """ Clone a repository from a URL into the target directory. - + Args: repo_url: URL of the repository to clone (e.g., https://github.com/owner/repo.git) target_dir: Directory to clone the repository into """ - + logger.info(f"Cloning repository from {repo_url} to {target_dir}") - + result = subprocess.run(["git", "clone", repo_url, target_dir]) if result.returncode != 0: logger.fatal(f"Failed to clone repository from {repo_url} to {target_dir}: {result.returncode}") - - logger.info(f"Successfully cloned repository from {repo_url} to {target_dir}") + logger.info(f"Successfully cloned repository from {repo_url} to {target_dir}") def clone_local_repo_at_commit(local_repo_dir: str, commit_hash: str, target_dir: str) -> None: """ Clone a local repository at a specific commit into the target directory. - + Args: - local_repo_dir: Path to the local repository + local_repo_dir: Path to the local repository commit_hash: The commit hash to clone from target_dir: Directory to clone the repository into """ - + # Make sure the local repository path exists if not os.path.exists(local_repo_dir): logger.fatal(f"Local repository directory does not exist: {local_repo_dir}") - + # Convert to absolute path to avoid issues with relative paths abs_local_repo_dir = os.path.abspath(local_repo_dir) - + # Clone the local repository directly to the target directory logger.debug(f"Cloning local repository from {local_repo_dir} to {target_dir}...") - + try: - subprocess.run( - ["git", "clone", abs_local_repo_dir, target_dir], - capture_output=True, - text=True, - check=True - ) + subprocess.run(["git", "clone", abs_local_repo_dir, target_dir], capture_output=True, text=True, check=True) except subprocess.CalledProcessError as e: logger.error(f"Failed to clone local repository from {abs_local_repo_dir} to {target_dir}") logger.error(f"Git clone exit code: {e.returncode}") logger.error(f"Git clone stdout: {e.stdout or ''}") logger.error(f"Git clone stderr: {e.stderr or ''}") raise - + logger.debug(f"Cloned local repository from {local_repo_dir} to {target_dir}") # Checkout the specific commit logger.debug(f"Checking out commit {commit_hash} in {target_dir}...") try: - subprocess.run( - ["git", "checkout", commit_hash], - capture_output=True, - text=True, - check=True, - cwd=target_dir - ) + subprocess.run(["git", "checkout", commit_hash], capture_output=True, text=True, check=True, cwd=target_dir) except subprocess.CalledProcessError as e: logger.error(f"Failed to checkout commit {commit_hash} in {target_dir}") logger.error(f"Git checkout exit code: {e.returncode}") logger.error(f"Git checkout stdout: {e.stdout or ''}") logger.error(f"Git checkout stderr: {e.stderr or ''}") raise - + logger.debug(f"Checked out commit {commit_hash} in {target_dir}") + def reset_local_repo_to_commit(local_repo_dir: str, commit_hash: str, target_dir: str) -> None: """ Reset a local repository to a specific commit and remove all future commits. This ensures that future commits cannot be accessed via git log or git show. - + Args: local_repo_dir: Path to the local repository commit_hash: The commit hash to reset to target_dir: Directory to reset the repository in """ - + # Make sure the local repository path exists if not os.path.exists(local_repo_dir): logger.fatal(f"Local repository directory does not exist: {local_repo_dir}") @@ -100,54 +89,42 @@ def reset_local_repo_to_commit(local_repo_dir: str, commit_hash: str, target_dir # Reset the repository to the specific commit logger.debug(f"Resetting local repository to commit {commit_hash} in {target_dir}...") - subprocess.run( - ["git", "reset", "--hard", commit_hash], - capture_output=True, - text=True, - check=True, - cwd=target_dir - ) - + subprocess.run(["git", "reset", "--hard", commit_hash], capture_output=True, text=True, check=True, cwd=target_dir) + logger.debug(f"Reset local repository to commit {commit_hash} in {target_dir}") - + # Remove all future commits by: # 1. Delete all branch refs that point to commits after the target commit # 2. Update HEAD to point directly to the commit (detached HEAD) # 3. Prune unreachable objects and garbage collect - + logger.debug(f"Removing future commits from {target_dir}...") - + # Delete all branch refs (we'll use detached HEAD instead) # This removes references to future commits try: - branches_result = subprocess.run( - ["git", "branch"], - capture_output=True, - text=True, - check=True, - cwd=target_dir - ) + branches_result = subprocess.run(["git", "branch"], capture_output=True, text=True, check=True, cwd=target_dir) # Parse branch names (remove leading * and whitespace) branches = [] - for line in branches_result.stdout.strip().split('\n'): + for line in branches_result.stdout.strip().split("\n"): line = line.strip() if line: # Remove leading * and whitespace - branch = line.lstrip('*').strip() + branch = line.lstrip("*").strip() if branch: branches.append(branch) - + for branch in branches: subprocess.run( ["git", "branch", "-D", branch], capture_output=True, text=True, check=False, # Don't fail if branch doesn't exist - cwd=target_dir + cwd=target_dir, ) except subprocess.CalledProcessError: pass # No branches to delete - + # Delete all remote refs (they might point to future commits) try: subprocess.run( @@ -155,42 +132,26 @@ def reset_local_repo_to_commit(local_repo_dir: str, commit_hash: str, target_dir capture_output=True, text=True, check=False, # Don't fail if remote doesn't exist - cwd=target_dir + cwd=target_dir, ) except Exception: pass - + # Delete all tag refs (they might point to future commits) try: - tags_result = subprocess.run( - ["git", "tag", "-l"], - capture_output=True, - text=True, - check=True, - cwd=target_dir - ) - tags = [t.strip() for t in tags_result.stdout.strip().split('\n') if t.strip()] + tags_result = subprocess.run(["git", "tag", "-l"], capture_output=True, text=True, check=True, cwd=target_dir) + tags = [t.strip() for t in tags_result.stdout.strip().split("\n") if t.strip()] for tag in tags: - subprocess.run( - ["git", "tag", "-d", tag], - capture_output=True, - text=True, - check=False, - cwd=target_dir - ) + subprocess.run(["git", "tag", "-d", tag], capture_output=True, text=True, check=False, cwd=target_dir) except subprocess.CalledProcessError: pass # No tags to delete - + # Set HEAD to point directly to the commit (detached HEAD state) # This ensures HEAD points to the exact commit, not a branch subprocess.run( - ["git", "checkout", "--detach", commit_hash], - capture_output=True, - text=True, - check=True, - cwd=target_dir + ["git", "checkout", "--detach", commit_hash], capture_output=True, text=True, check=True, cwd=target_dir ) - + # Prune unreachable objects and garbage collect to actually remove them # This physically deletes commits that are no longer reachable subprocess.run( @@ -198,43 +159,34 @@ def reset_local_repo_to_commit(local_repo_dir: str, commit_hash: str, target_dir capture_output=True, text=True, check=False, # May fail if no reflog exists - cwd=target_dir + cwd=target_dir, ) - + subprocess.run( - ["git", "gc", "--prune=now", "--aggressive"], - capture_output=True, - text=True, - check=True, - cwd=target_dir + ["git", "gc", "--prune=now", "--aggressive"], capture_output=True, text=True, check=True, cwd=target_dir ) - + logger.debug(f"Removed future commits from {target_dir}") def verify_commit_exists_in_local_repo(local_repo_dir: str, commit_hash: str) -> bool: """ Verify that a specific commit exists in the repository. - + Args: local_repo_dir: Path to the local repository commit_hash: The commit hash to verify - + Returns: bool: True if commit exists, False otherwise """ - + # Make sure the local repository directory exists if not os.path.exists(local_repo_dir): return False - + # Use `git cat-file -e` to verify that the commit exists - result = subprocess.run( - ["git", "cat-file", "-e", commit_hash], - capture_output=True, - text=True, - cwd=local_repo_dir - ) + result = subprocess.run(["git", "cat-file", "-e", commit_hash], capture_output=True, text=True, cwd=local_repo_dir) # `git cat-file -e` return codes: # 0: commit exists @@ -242,11 +194,10 @@ def verify_commit_exists_in_local_repo(local_repo_dir: str, commit_hash: str) -> return result.returncode == 0 - def init_local_repo_with_initial_commit(local_repo_dir: str, commit_message: str = "Initial commit") -> None: """ Initialize a Git repository in the given directory and make an initial commit with all the files in the directory. - + Args: directory: Path to the directory to initialize as a Git repo commit_message: Commit message for the initial commit (default: "Initial commit") @@ -254,35 +205,19 @@ def init_local_repo_with_initial_commit(local_repo_dir: str, commit_message: str # Initialize git repository logger.debug(f"Initializing git repository in {local_repo_dir}") - subprocess.run( - ['git', 'init'], - capture_output=True, - text=True, - check=True, - cwd=local_repo_dir - ) + subprocess.run(["git", "init"], capture_output=True, text=True, check=True, cwd=local_repo_dir) logger.debug(f"Initialized git repository in {local_repo_dir}") # Add all files logger.debug(f"Adding all files in {local_repo_dir}") - subprocess.run( - ['git', 'add', '.'], - capture_output=True, - text=True, - check=True, - cwd=local_repo_dir - ) + subprocess.run(["git", "add", "."], capture_output=True, text=True, check=True, cwd=local_repo_dir) logger.debug(f"Added all files in {local_repo_dir}") - + # Make initial commit logger.debug(f"Making initial commit in {local_repo_dir}: {commit_message}") try: subprocess.run( - ['git', 'commit', '-m', commit_message], - capture_output=True, - text=True, - check=True, - cwd=local_repo_dir + ["git", "commit", "-m", commit_message], capture_output=True, text=True, check=True, cwd=local_repo_dir ) logger.debug(f"Made initial commit in {local_repo_dir}: {commit_message}") except subprocess.CalledProcessError as e: @@ -293,11 +228,10 @@ def init_local_repo_with_initial_commit(local_repo_dir: str, commit_message: str raise - def reset_local_repo(local_repo_dir: str, commit_hash: str) -> None: """ Resets the local repository to the specified commit hash. - + Args: local_repo_dir: Path to the local repository commit_hash: The commit hash to reset to @@ -312,17 +246,17 @@ def reset_local_repo(local_repo_dir: str, commit_hash: str) -> None: logger.info(f"Reset {local_repo_dir} to commit {commit_hash}") - def get_local_repo_commit_hash(local_repo_dir: str) -> str: """ Get the commit hash of the current commit in the local repository. - + Args: local_repo_dir: Path to the local repository """ - - return subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True, text=True, check=True, cwd=local_repo_dir).stdout.strip() + return subprocess.run( + ["git", "rev-parse", "HEAD"], capture_output=True, text=True, check=True, cwd=local_repo_dir + ).stdout.strip() -COMMIT_HASH = get_local_repo_commit_hash(pathlib.Path(__file__).parent.parent) \ No newline at end of file +COMMIT_HASH = get_local_repo_commit_hash(pathlib.Path(__file__).parent.parent) diff --git a/utils/logger.py b/utils/logger.py index e1f9c484f..9fa32ef6c 100644 --- a/utils/logger.py +++ b/utils/logger.py @@ -1,56 +1,54 @@ -import os import inspect import logging - +import os from datetime import datetime - - # We want some loggers from third-party libraries to be quieter -logging.getLogger('httpx').setLevel(logging.WARNING) -logging.getLogger('chain_utils').setLevel(logging.WARNING) - +logging.getLogger("httpx").setLevel(logging.WARNING) +logging.getLogger("chain_utils").setLevel(logging.WARNING) LEVEL_NAME_TO_COLOR = { - 'DEBUG': '\033[90m', # Gray - 'INFO': '\033[32m', # Green - 'WARNING': '\033[33m', # Yellow - 'ERROR': '\033[31m', # Red - 'FATAL': '\033[31m' # Red + "DEBUG": "\033[90m", # Gray + "INFO": "\033[32m", # Green + "WARNING": "\033[33m", # Yellow + "ERROR": "\033[31m", # Red + "FATAL": "\033[31m", # Red } -GRAY = '\033[90m' -RESET = '\033[0m' - +GRAY = "\033[90m" +RESET = "\033[0m" def print_log(level: str, message: str): now = datetime.now() - timestamp = now.strftime('%Y-%m-%d %H:%M:%S') + timestamp = now.strftime("%Y-%m-%d %H:%M:%S") ms = now.microsecond // 1000 frame = inspect.currentframe().f_back - file = frame.f_code.co_filename.split('/')[-1] + file = frame.f_code.co_filename.split("/")[-1] line = frame.f_lineno - - print(f"{timestamp}.{ms:03d} - {file}:{line} - [{LEVEL_NAME_TO_COLOR[level]}{level}{RESET}] - {message}") + print(f"{timestamp}.{ms:03d} - {file}:{line} - [{LEVEL_NAME_TO_COLOR[level]}{level}{RESET}] - {message}") def debug(message: str): - if os.getenv('DEBUG', 'false').lower() == 'true': - print_log('DEBUG', GRAY + message + RESET) + if os.getenv("DEBUG", "false").lower() == "true": + print_log("DEBUG", GRAY + message + RESET) + def info(message: str): - print_log('INFO', message) + print_log("INFO", message) + def warning(message: str): - print_log('WARNING', message) + print_log("WARNING", message) + def error(message: str): - print_log('ERROR', message) + print_log("ERROR", message) + def fatal(message: str): - print_log('FATAL', message) - raise Exception(message) \ No newline at end of file + print_log("FATAL", message) + raise Exception(message) diff --git a/utils/s3.py b/utils/s3.py index 083b04158..31073748b 100644 --- a/utils/s3.py +++ b/utils/s3.py @@ -1,6 +1,6 @@ import aioboto3 -import utils.logger as logger +import utils.logger as logger async def initialize_s3(*, _bucket: str, region: str, access_key_id: str, secret_access_key: str): @@ -8,17 +8,16 @@ async def initialize_s3(*, _bucket: str, region: str, access_key_id: str, secret global session, bucket session = aioboto3.Session( - aws_access_key_id=access_key_id, - aws_secret_access_key=secret_access_key, - region_name=region + aws_access_key_id=access_key_id, aws_secret_access_key=secret_access_key, region_name=region ) bucket = _bucket logger.info(f"S3 client initialized for bucket {_bucket}.") + async def deinitialize_s3(): logger.info("Deinitializing S3 client...") - + global session, bucket session = None bucket = None @@ -26,22 +25,22 @@ async def deinitialize_s3(): logger.info("S3 client deinitialized.") - async def upload_text_file_to_s3(path: str, text: str): global session, bucket - - async with session.client('s3') as s3_client: + + async with session.client("s3") as s3_client: logger.info(f"Uploading text file to s3://{bucket}/{path}") - await s3_client.put_object(Bucket=bucket, Key=path, Body=text.encode('utf-8')) + await s3_client.put_object(Bucket=bucket, Key=path, Body=text.encode("utf-8")) logger.info(f"Successfully uploaded text file to s3://{bucket}/{path}") + async def download_text_file_from_s3(path: str) -> str: global session, bucket - - async with session.client('s3') as s3_client: + + async with session.client("s3") as s3_client: logger.info(f"Downloading text file from s3://{bucket}/{path}") response = await s3_client.get_object(Bucket=bucket, Key=path) - body = await response['Body'].read() - content = body.decode('utf-8') + body = await response["Body"].read() + content = body.decode("utf-8") logger.info(f"Successfully downloaded text file from s3://{bucket}/{path}") - return content \ No newline at end of file + return content diff --git a/utils/system_metrics.py b/utils/system_metrics.py index f7f97f766..f7075b983 100644 --- a/utils/system_metrics.py +++ b/utils/system_metrics.py @@ -1,10 +1,10 @@ -import psutil -import utils.logger as logger - from typing import Optional + +import psutil from pydantic import BaseModel -from utils.docker import get_num_docker_containers +import utils.logger as logger +from utils.docker import get_num_docker_containers class SystemMetrics(BaseModel): @@ -25,7 +25,6 @@ class SystemMetrics(BaseModel): num_containers: Optional[int] = None - async def get_system_metrics() -> SystemMetrics: metrics = SystemMetrics() @@ -34,15 +33,15 @@ async def get_system_metrics() -> SystemMetrics: memory = psutil.virtual_memory() metrics.ram_percent = memory.percent - metrics.ram_total_gb = memory.total / (1000 ** 3) + metrics.ram_total_gb = memory.total / (1000**3) - disk = psutil.disk_usage('/') + disk = psutil.disk_usage("/") metrics.disk_percent = disk.percent - metrics.disk_total_gb = disk.total / (1000 ** 3) + metrics.disk_total_gb = disk.total / (1000**3) metrics.num_containers = get_num_docker_containers() except Exception as e: logger.warning(f"Error in get_system_metrics(): {e}") - - return metrics \ No newline at end of file + + return metrics diff --git a/utils/temp.py b/utils/temp.py index eb8948092..8674d0866 100644 --- a/utils/temp.py +++ b/utils/temp.py @@ -4,15 +4,13 @@ import tempfile - def create_temp_dir(): """Create a temporary directory.""" return tempfile.mkdtemp() - def delete_temp_dir(temp_dir: str): """Delete a temporary directory.""" - - shutil.rmtree(temp_dir, ignore_errors=True) \ No newline at end of file + + shutil.rmtree(temp_dir, ignore_errors=True) diff --git a/utils/ttl.py b/utils/ttl.py index 1d112e6d0..50a1d6b14 100644 --- a/utils/ttl.py +++ b/utils/ttl.py @@ -1,26 +1,24 @@ import asyncio -import utils.logger as logger - +from datetime import datetime, timedelta, timezone from functools import wraps -from pydantic import BaseModel, ConfigDict -from datetime import datetime, timezone,timedelta -from typing import Any, Dict, Tuple, Callable, TypeAlias +from typing import Any, Callable, Dict, Tuple, TypeAlias +from pydantic import BaseModel, ConfigDict +import utils.logger as logger TTLCacheKey: TypeAlias = Tuple[Any, ...] + def _args_and_kwargs_to_ttl_cache_key(args: Tuple, kwargs: Dict) -> TTLCacheKey: return (args, tuple(sorted(kwargs.items()))) - class TTLCacheEntry(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) expires_at: datetime value: Any - # NOTE ADAM: A robust TTL cache implementation. The implementation supports the following @@ -54,7 +52,6 @@ def decorator(func: Callable): cache: Dict[TTLCacheKey, TTLCacheEntry] = {} recalculating_locks: Dict[TTLCacheKey, asyncio.Lock] = {} - def _evict_expired(): """Remove all expired entries and their locks.""" now = datetime.now(timezone.utc) @@ -71,7 +68,6 @@ def _evict_expired(): del cache[k] recalculating_locks.pop(k, None) - @wraps(func) async def wrapper(*args, **kwargs): key = _args_and_kwargs_to_ttl_cache_key(args, kwargs) @@ -99,33 +95,28 @@ async def wrapper(*args, **kwargs): if lock.locked(): logger.debug(f"[TTLCache] {func.__name__}(): First request, already calculating, started waiting") async with lock: - logger.debug(f"[TTLCache] {func.__name__}(): First request, already calculating, stopped waiting") + logger.debug( + f"[TTLCache] {func.__name__}(): First request, already calculating, stopped waiting" + ) return cache[key].value else: logger.debug(f"[TTLCache] {func.__name__}(): First request, triggering calculation") await _recalculate(args, kwargs) return cache[key].value - - async def _recalculate(args: Tuple, kwargs: Dict): key = _args_and_kwargs_to_ttl_cache_key(args, kwargs) async with recalculating_locks[key]: if key in cache and datetime.now(timezone.utc) < cache[key].expires_at: return - + value = await func(*args, **kwargs) cache[key] = TTLCacheEntry( - expires_at=datetime.now(timezone.utc) + timedelta(seconds=ttl_seconds), - value=value + expires_at=datetime.now(timezone.utc) + timedelta(seconds=ttl_seconds), value=value ) logger.debug(f"[TTLCache] {func.__name__}(): Calculation completed") - - return wrapper - - return decorator diff --git a/utils/validator_hotkeys.py b/utils/validator_hotkeys.py index a681bd15f..d41c44d3d 100644 --- a/utils/validator_hotkeys.py +++ b/utils/validator_hotkeys.py @@ -1,30 +1,41 @@ # List of whitelisted validators (including their names and hotkeys) WHITELISTED_VALIDATORS = [ - {"name": "RoundTable 21", "short_name": "RT21", "hotkey": "5Djyacas3eWLPhCKsS3neNSJonzfxJmD3gcrMTFDc4eHsn62"}, - {"name": "Uncle Tao", "hotkey": "5FF1rU17iEYzMYS7V59P6mK2PFtz9wDUoUKrpFd3yw1wBcfq"}, - {"name": "Yuma", "hotkey": "5Eho9y6iF5aTdKS28Awn2pKTd4dFsJ2o3shGtj1vjnLiaKJ1"}, - {"name": "Rizzo", "hotkey": "5GuRsre3hqm6WKWRCqVxXdM4UtGs457nDhPo9F5wvJ16Ys62"}, - {"name": "Ridges", "hotkey": "5GgJptBaUiWwb8SQDinZ9rDQoVw47mgduXaCLHeJGTtA4JMS"}, - {"name": "Crucible Labs", "short_name": "Crucible", "hotkey": "5HmkM6X1D3W3CuCSPuHhrbYyZNBy2aGAiZy9NczoJmtY25H7"}, - {"name": "tao.bot", "hotkey": "5E2LP6EnZ54m3wS8s1yPvD5c3xo71kQroBw7aUVK32TKeZ5u"}, - {"name": "Opentensor Foundation", "short_name": "OTF", "hotkey": "5FZ1BFw8eRMAFK5zwJdyefrsn51Lrm217WKbo3MmdFH65YRr"}, - {"name": "Kraken", "hotkey": "5G8iwBWxPjCfu9Fc3jFP37j1Ax5KypDDmUPUSoS9aWAsSCGT"}, - {"name": "TAO.com", "hotkey": "5DP7gcGeTfGfmCzxVNCXQvWBxu58TrrkdikuHQNnGDYb7THU"}, - {"name": "TAOApp", "hotkey": "5F4U4P2j3ctdDS45naSUCxzYAHWTHarsY5JQdcRmMkc8UQZM"}, - + {"name": "RoundTable 21", "short_name": "RT21", "hotkey": "5Djyacas3eWLPhCKsS3neNSJonzfxJmD3gcrMTFDc4eHsn62"}, + {"name": "Uncle Tao", "hotkey": "5FF1rU17iEYzMYS7V59P6mK2PFtz9wDUoUKrpFd3yw1wBcfq"}, + {"name": "Yuma", "hotkey": "5Eho9y6iF5aTdKS28Awn2pKTd4dFsJ2o3shGtj1vjnLiaKJ1"}, + {"name": "Rizzo", "hotkey": "5GuRsre3hqm6WKWRCqVxXdM4UtGs457nDhPo9F5wvJ16Ys62"}, + {"name": "Ridges", "hotkey": "5GgJptBaUiWwb8SQDinZ9rDQoVw47mgduXaCLHeJGTtA4JMS"}, + {"name": "Crucible Labs", "short_name": "Crucible", "hotkey": "5HmkM6X1D3W3CuCSPuHhrbYyZNBy2aGAiZy9NczoJmtY25H7"}, + {"name": "tao.bot", "hotkey": "5E2LP6EnZ54m3wS8s1yPvD5c3xo71kQroBw7aUVK32TKeZ5u"}, + { + "name": "Opentensor Foundation", + "short_name": "OTF", + "hotkey": "5FZ1BFw8eRMAFK5zwJdyefrsn51Lrm217WKbo3MmdFH65YRr", + }, + {"name": "Kraken", "hotkey": "5G8iwBWxPjCfu9Fc3jFP37j1Ax5KypDDmUPUSoS9aWAsSCGT"}, + {"name": "TAO.com", "hotkey": "5DP7gcGeTfGfmCzxVNCXQvWBxu58TrrkdikuHQNnGDYb7THU"}, + {"name": "TAOApp", "hotkey": "5F4U4P2j3ctdDS45naSUCxzYAHWTHarsY5JQdcRmMkc8UQZM"}, # Developer validators, used for testing - {"name": "Adam's Validator", "hotkey": "5Dy9FDg5jshHS7MirAFrRsKiFa6GPRMaiHC4Zng4HAgyi8yf"}, - {"name": "Alex's Validator (1)", "hotkey": "5HpMvcM593HmizCA3ARLNifxjPSLbN3M5RHYy4GiEqmB3x9n"}, - {"name": "Alex's Validator (2)", "hotkey": "5HNpAXVzWaW4yD9UqH5sXFPt1gPFqNTViDy61NdiViyDQiTQ"}, - {"name": "Alex's Validator (3)", "hotkey": "5GgqnYQ3QwnCcmxiGatXS3rrHGmkqU3cMSjQFSdLKHDmxyB6"}, - {"name": "Shak's Validator", "hotkey": "5F26aNVC3rZVNbH36DWdZzxPVH17iBNGD14Wtb4nQem742Q7"} + {"name": "Adam's Validator", "hotkey": "5Dy9FDg5jshHS7MirAFrRsKiFa6GPRMaiHC4Zng4HAgyi8yf"}, + {"name": "Alex's Validator (1)", "hotkey": "5HpMvcM593HmizCA3ARLNifxjPSLbN3M5RHYy4GiEqmB3x9n"}, + {"name": "Alex's Validator (2)", "hotkey": "5HNpAXVzWaW4yD9UqH5sXFPt1gPFqNTViDy61NdiViyDQiTQ"}, + {"name": "Alex's Validator (3)", "hotkey": "5GgqnYQ3QwnCcmxiGatXS3rrHGmkqU3cMSjQFSdLKHDmxyB6"}, + {"name": "Shak's Validator", "hotkey": "5F26aNVC3rZVNbH36DWdZzxPVH17iBNGD14Wtb4nQem742Q7"}, ] + def is_validator_hotkey_whitelisted(validator_hotkey: str) -> bool: return validator_hotkey in [validator["hotkey"] for validator in WHITELISTED_VALIDATORS] + def validator_name_to_hotkey(validator_name: str) -> str: - return next((validator["hotkey"] for validator in WHITELISTED_VALIDATORS if validator["name"] == validator_name), 'unknown') + return next( + (validator["hotkey"] for validator in WHITELISTED_VALIDATORS if validator["name"] == validator_name), "unknown" + ) + def validator_hotkey_to_name(validator_hotkey: str) -> str: - return next((validator["name"] for validator in WHITELISTED_VALIDATORS if validator["hotkey"] == validator_hotkey), 'unknown') + return next( + (validator["name"] for validator in WHITELISTED_VALIDATORS if validator["hotkey"] == validator_hotkey), + "unknown", + ) diff --git a/validator/config.py b/validator/config.py index 93638445f..b5363be29 100644 --- a/validator/config.py +++ b/validator/config.py @@ -1,16 +1,14 @@ import os import re -import utils.logger as logger -from dotenv import load_dotenv from bittensor_wallet.wallet import Wallet +from dotenv import load_dotenv - +import utils.logger as logger load_dotenv() - NETUID = os.getenv("NETUID") if not NETUID: logger.fatal("NETUID is not set in .env") @@ -25,7 +23,6 @@ logger.fatal("SUBTENSOR_NETWORK is not set in .env") - MODE = os.getenv("MODE") if not MODE: logger.fatal("MODE is not set in .env") @@ -34,7 +31,6 @@ logger.fatal("MODE must be either 'screener' or 'validator'") - if MODE == "validator": VALIDATOR_WALLET_NAME = os.getenv("VALIDATOR_WALLET_NAME") if not VALIDATOR_WALLET_NAME: @@ -51,7 +47,6 @@ logger.fatal(f"Error loading hotkey: {e}") - elif MODE == "screener": SCREENER_NAME = os.getenv("SCREENER_NAME") if not SCREENER_NAME: @@ -69,7 +64,6 @@ logger.fatal("SCREENER_PASSWORD is not set in .env") - RIDGES_PLATFORM_URL = os.getenv("RIDGES_PLATFORM_URL") if not RIDGES_PLATFORM_URL: logger.fatal("RIDGES_PLATFORM_URL is not set in .env") @@ -86,27 +80,25 @@ logger.fatal("RIDGES_INFERENCE_GATEWAY_URL must be set to a local IP address, not localhost/127.0.0.1.") - SEND_HEARTBEAT_INTERVAL_SECONDS = os.getenv("SEND_HEARTBEAT_INTERVAL_SECONDS") if not SEND_HEARTBEAT_INTERVAL_SECONDS: logger.fatal("SEND_HEARTBEAT_INTERVAL_SECONDS is not set in .env") -SEND_HEARTBEAT_INTERVAL_SECONDS = max(int(SEND_HEARTBEAT_INTERVAL_SECONDS), 10) # minimum 10 seconds +SEND_HEARTBEAT_INTERVAL_SECONDS = max(int(SEND_HEARTBEAT_INTERVAL_SECONDS), 10) # minimum 10 seconds SET_WEIGHTS_INTERVAL_SECONDS = os.getenv("SET_WEIGHTS_INTERVAL_SECONDS") if not SET_WEIGHTS_INTERVAL_SECONDS: logger.fatal("SET_WEIGHTS_INTERVAL_SECONDS is not set in .env") -SET_WEIGHTS_INTERVAL_SECONDS = int(SET_WEIGHTS_INTERVAL_SECONDS) +SET_WEIGHTS_INTERVAL_SECONDS = int(SET_WEIGHTS_INTERVAL_SECONDS) -SET_WEIGHTS_TIMEOUT_SECONDS = os.getenv("SET_WEIGHTS_TIMEOUT_SECONDS", "90") # TODO ADAM +SET_WEIGHTS_TIMEOUT_SECONDS = os.getenv("SET_WEIGHTS_TIMEOUT_SECONDS", "90") # TODO ADAM if not SET_WEIGHTS_TIMEOUT_SECONDS: logger.fatal("SET_WEIGHTS_TIMEOUT_SECONDS is not set in .env") -SET_WEIGHTS_TIMEOUT_SECONDS = int(SET_WEIGHTS_TIMEOUT_SECONDS) +SET_WEIGHTS_TIMEOUT_SECONDS = int(SET_WEIGHTS_TIMEOUT_SECONDS) REQUEST_EVALUATION_INTERVAL_SECONDS = os.getenv("REQUEST_EVALUATION_INTERVAL_SECONDS") if not REQUEST_EVALUATION_INTERVAL_SECONDS: logger.fatal("REQUEST_EVALUATION_INTERVAL_SECONDS is not set in .env") -REQUEST_EVALUATION_INTERVAL_SECONDS = int(REQUEST_EVALUATION_INTERVAL_SECONDS) - +REQUEST_EVALUATION_INTERVAL_SECONDS = int(REQUEST_EVALUATION_INTERVAL_SECONDS) SIMULATE_EVALUATION_RUNS = os.getenv("SIMULATE_EVALUATION_RUNS") @@ -125,14 +117,12 @@ INCLUDE_SOLUTIONS = INCLUDE_SOLUTIONS.lower() == "true" - UPDATE_AUTOMATICALLY = os.getenv("UPDATE_AUTOMATICALLY") if not UPDATE_AUTOMATICALLY: logger.fatal("UPDATE_AUTOMATICALLY is not set in .env") UPDATE_AUTOMATICALLY = UPDATE_AUTOMATICALLY.lower() == "true" - logger.info("=== Validator Configuration ===") logger.info(f"Network ID: {NETUID}") @@ -176,7 +166,7 @@ MAX_CONCURRENT_EVALUATION_RUNS = os.getenv("MAX_CONCURRENT_EVALUATION_RUNS") if not MAX_CONCURRENT_EVALUATION_RUNS: logger.warning("MAX_CONCURRENT_EVALUATION_RUNS is not set in .env") - MAX_CONCURRENT_EVALUATION_RUNS = 30 # high default for validators + MAX_CONCURRENT_EVALUATION_RUNS = 30 # high default for validators MAX_CONCURRENT_EVALUATION_RUNS = int(MAX_CONCURRENT_EVALUATION_RUNS) logger.info("===============================") diff --git a/validator/http_utils.py b/validator/http_utils.py index c2a359205..01ab5892c 100644 --- a/validator/http_utils.py +++ b/validator/http_utils.py @@ -1,19 +1,17 @@ import json -import httpx import textwrap -import utils.logger as logger -import validator.config as config - from typing import Any -from pydantic import BaseModel +import httpx +from pydantic import BaseModel +import utils.logger as logger +import validator.config as config # TODO ADAM: .env HTTP_TIMEOUT_SECONDS = 120 - def _pretty_print_httpx_error(method: str, url: str, e: httpx.HTTPStatusError): # HTTP error (4xx or 5xx) logger.error(f"HTTP {e.response.status_code} {e.response.reason_phrase} during {method} {url}") @@ -21,20 +19,24 @@ def _pretty_print_httpx_error(method: str, url: str, e: httpx.HTTPStatusError): # Try and print the response as best as we can try: response_json = e.response.json() - if isinstance(response_json, dict) and len(response_json) == 1 and "detail" in response_json and isinstance(response_json["detail"], str): + if ( + isinstance(response_json, dict) + and len(response_json) == 1 + and "detail" in response_json + and isinstance(response_json["detail"], str) + ): # The response is a JSON that looks like {"detail": "..."} logger.error(textwrap.indent(response_json["detail"], " ")) else: # The response is a JSON - logger.error(f"Response (JSON):") + logger.error("Response (JSON):") logger.error(textwrap.indent(json.dumps(response_json, indent=2), " ")) except Exception: # The response is not a JSON - logger.error(f"Response:") + logger.error("Response:") logger.error(textwrap.indent(e.response.text, " ")) - async def get_ridges_platform(endpoint: str, *, quiet: int = 0) -> Any: """ Helper function that sends a GET request to the Ridges platform. @@ -50,8 +52,6 @@ async def get_ridges_platform(endpoint: str, *, quiet: int = 0) -> Any: The response from the Ridges platform. If the request returns a non-2xx status code, the function will print the error and exit the program. """ - - url = f"{config.RIDGES_PLATFORM_URL.rstrip('/')}/{endpoint.lstrip('/')}" if quiet <= 1: @@ -68,14 +68,14 @@ async def get_ridges_platform(endpoint: str, *, quiet: int = 0) -> Any: logger.debug(f"Received response for GET {url}: {response.status_code} {response.reason_phrase}") if response_json != {} and quiet == 0: logger.debug(textwrap.indent(json.dumps(response_json, indent=2), " ")) - + return response.json() - + except httpx.HTTPStatusError as e: _pretty_print_httpx_error("GET", url, e) - + raise - + except Exception as e: # Internal error (timeout, DNS error, etc.) logger.error(f"{type(e).__name__} during GET {url}") @@ -83,8 +83,9 @@ async def get_ridges_platform(endpoint: str, *, quiet: int = 0) -> Any: raise - -async def post_ridges_platform(endpoint: str, body: BaseModel, *, bearer_token: str = None, quiet: int = 0, timeout: int = HTTP_TIMEOUT_SECONDS) -> Any: +async def post_ridges_platform( + endpoint: str, body: BaseModel, *, bearer_token: str = None, quiet: int = 0, timeout: int = HTTP_TIMEOUT_SECONDS +) -> Any: """ Helper function that sends a POST request to the Ridges platform. @@ -101,8 +102,6 @@ async def post_ridges_platform(endpoint: str, body: BaseModel, *, bearer_token: The response from the Ridges platform. If the request returns a non-2xx status code, the function will print the error and exit the program. """ - - url = f"{config.RIDGES_PLATFORM_URL.rstrip('/')}/{endpoint.lstrip('/')}" body_dict = body.model_dump(mode="json") @@ -111,7 +110,7 @@ async def post_ridges_platform(endpoint: str, body: BaseModel, *, bearer_token: logger.debug(f"Sending request for POST {url}") if body_dict != {} and quiet == 0: logger.debug(textwrap.indent(json.dumps(body_dict, indent=2), " ")) - + try: # Send the request async with httpx.AsyncClient(timeout=timeout) as client: @@ -124,16 +123,16 @@ async def post_ridges_platform(endpoint: str, body: BaseModel, *, bearer_token: logger.debug(f"Received response for POST {url}: {response.status_code} {response.reason_phrase}") if response_json != {} and quiet == 0: logger.debug(textwrap.indent(json.dumps(response_json, indent=2), " ")) - + return response.json() - + except httpx.HTTPStatusError as e: _pretty_print_httpx_error("POST", url, e) - + raise - + except Exception as e: # Internal error (timeout, DNS error, etc.) logger.error(f"{type(e).__name__} during POST {url}") - raise \ No newline at end of file + raise diff --git a/validator/main.py b/validator/main.py index d5f84ab6a..971f30902 100644 --- a/validator/main.py +++ b/validator/main.py @@ -1,31 +1,41 @@ # NOTE ADAM: Subtensor bug (self.disable_third_party_loggers()) -from validator.set_weights import set_weights_from_mapping - +import asyncio import os +import pathlib +import random import sys import time -import httpx -import random -import asyncio -import pathlib import traceback +from typing import Any, Dict +from uuid import UUID + +import httpx + import utils.logger as logger import validator.config as config - -from typing import Any, Dict -from api.endpoints.validator_models import * +from api.endpoints.validator_models import ( + ScreenerRegistrationRequest, + ScreenerRegistrationResponse, + ValidatorDisconnectRequest, + ValidatorFinishEvaluationRequest, + ValidatorHeartbeatRequest, + ValidatorRegistrationRequest, + ValidatorRegistrationResponse, + ValidatorRequestEvaluationRequest, + ValidatorRequestEvaluationResponse, + ValidatorUpdateEvaluationRunRequest, +) +from evaluator.models import EvaluationRunException +from evaluator.problem_suites.polyglot.polyglot_suite import POLYGLOT_JS_SUITE, POLYGLOT_PY_SUITE +from evaluator.problem_suites.swebench_verified.swebench_verified_suite import SWEBENCH_VERIFIED_SUITE +from evaluator.sandbox.sandbox_manager import SandboxManager +from models.evaluation_run import EvaluationRunErrorCode, EvaluationRunStatus +from models.evaluation_set import EvaluationSetProblem from models.problem import ProblemTestResultStatus from utils.git import COMMIT_HASH, reset_local_repo -from evaluator.models import EvaluationRunException from utils.system_metrics import get_system_metrics -from models.evaluation_set import EvaluationSetProblem -from evaluator.sandbox.sandbox_manager import SandboxManager from validator.http_utils import get_ridges_platform, post_ridges_platform -from models.evaluation_run import EvaluationRunStatus, EvaluationRunErrorCode -from evaluator.problem_suites.polyglot.polyglot_suite import POLYGLOT_PY_SUITE, POLYGLOT_JS_SUITE -from evaluator.problem_suites.swebench_verified.swebench_verified_suite import SWEBENCH_VERIFIED_SUITE - - +from validator.set_weights import set_weights_from_mapping # The session ID for this validator session_id = None @@ -34,21 +44,21 @@ max_evaluation_run_log_size_bytes = None - # The sandbox manager and problem suites sandbox_manager = None problem_suites = [] - # Disconnect from the Ridges platform (called when the program exits) async def disconnect(reason: str): if session_id is None: return - + try: logger.info("Disconnecting validator...") - await post_ridges_platform("/validator/disconnect", ValidatorDisconnectRequest(reason=reason), bearer_token=session_id) + await post_ridges_platform( + "/validator/disconnect", ValidatorDisconnectRequest(reason=reason), bearer_token=session_id + ) logger.info("Disconnected validator") except Exception as e: logger.error(f"Error in disconnect(): {type(e).__name__}: {e}") @@ -56,7 +66,6 @@ async def disconnect(reason: str): os._exit(1) - # A loop that sends periodic heartbeats to the Ridges platform async def send_heartbeat_loop(): try: @@ -64,60 +73,75 @@ async def send_heartbeat_loop(): while True: logger.info("Sending heartbeat...") system_metrics = await get_system_metrics() - await post_ridges_platform("/validator/heartbeat", ValidatorHeartbeatRequest(system_metrics=system_metrics), bearer_token=session_id, quiet=2, timeout=5) + await post_ridges_platform( + "/validator/heartbeat", + ValidatorHeartbeatRequest(system_metrics=system_metrics), + bearer_token=session_id, + quiet=2, + timeout=5, + ) await asyncio.sleep(config.SEND_HEARTBEAT_INTERVAL_SECONDS) except Exception as e: logger.error(f"Error in send_heartbeat_loop(): {type(e).__name__}: {e}") logger.error(traceback.format_exc()) os._exit(1) + # A loop that periodically sets weights async def set_weights_loop(): logger.info("Starting set weights loop...") while True: weights_mapping = await get_ridges_platform("/scoring/weights", quiet=1) - + try: - await asyncio.wait_for(set_weights_from_mapping(weights_mapping), timeout=config.SET_WEIGHTS_TIMEOUT_SECONDS) + await asyncio.wait_for( + set_weights_from_mapping(weights_mapping), timeout=config.SET_WEIGHTS_TIMEOUT_SECONDS + ) except asyncio.TimeoutError as e: logger.error(f"asyncio.TimeoutError in set_weights_from_mapping(): {e}") await asyncio.sleep(config.SET_WEIGHTS_INTERVAL_SECONDS) - # Sends an update-evaluation-run request to the Ridges platform. The extra # parameter is for fields that are not sent in all requests, such as agent_logs # and eval_logs, which are only sent on some state transitions. -async def update_evaluation_run(evaluation_run_id: UUID, problem_name: str, updated_status: EvaluationRunStatus, extra: Dict[str, Any] = {}): +async def update_evaluation_run( + evaluation_run_id: UUID, problem_name: str, updated_status: EvaluationRunStatus, extra: Dict[str, Any] = {} +): logger.info(f"Updating evaluation run {evaluation_run_id} for problem {problem_name} to {updated_status.value}...") - - await post_ridges_platform("/validator/update-evaluation-run", ValidatorUpdateEvaluationRunRequest( - evaluation_run_id=evaluation_run_id, - updated_status=updated_status, - **(extra or {}) - ), bearer_token=session_id, quiet=2) + await post_ridges_platform( + "/validator/update-evaluation-run", + ValidatorUpdateEvaluationRunRequest( + evaluation_run_id=evaluation_run_id, updated_status=updated_status, **(extra or {}) + ), + bearer_token=session_id, + quiet=2, + ) # Truncates a log if required def truncate_logs_if_required(log: str) -> str: if len(log) > max_evaluation_run_log_size_bytes: - return f"\n\n" + log[-max_evaluation_run_log_size_bytes:] + return ( + f"\n\n" + + log[-max_evaluation_run_log_size_bytes:] + ) return log - -async def _simulate_run_evaluation_run_with_semaphore(evaluation_run_id: UUID, problem_name: str, semaphore: asyncio.Semaphore): +async def _simulate_run_evaluation_run_with_semaphore( + evaluation_run_id: UUID, problem_name: str, semaphore: asyncio.Semaphore +): async with semaphore: return await _simulate_run_evaluation_run(evaluation_run_id, problem_name) + # Simulate a run of an evaluation run, useful for testing, set SIMULATE_EVALUATION_RUNS=True in .env async def _simulate_run_evaluation_run(evaluation_run_id: UUID, problem_name: str): logger.info(f"Starting simulated evaluation run {evaluation_run_id} for problem {problem_name}...") - - # Move from pending -> initializing_agent await asyncio.sleep(random.random() * config.SIMULATE_EVALUATION_RUN_MAX_TIME_PER_STAGE_SECONDS) await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.initializing_agent) @@ -128,10 +152,12 @@ async def _simulate_run_evaluation_run(evaluation_run_id: UUID, problem_name: st # Move from running_agent -> initializing_eval await asyncio.sleep(random.random() * config.SIMULATE_EVALUATION_RUN_MAX_TIME_PER_STAGE_SECONDS) - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.initializing_eval, { - "patch": "FAKE PATCH", - "agent_logs": "FAKE AGENT LOGS" - }) + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.initializing_eval, + {"patch": "FAKE PATCH", "agent_logs": "FAKE AGENT LOGS"}, + ) # Move from initializing_eval -> running_eval await asyncio.sleep(random.random() * config.SIMULATE_EVALUATION_RUN_MAX_TIME_PER_STAGE_SECONDS) @@ -139,20 +165,28 @@ async def _simulate_run_evaluation_run(evaluation_run_id: UUID, problem_name: st # Move from running_eval -> finished await asyncio.sleep(random.random() * config.SIMULATE_EVALUATION_RUN_MAX_TIME_PER_STAGE_SECONDS) - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.finished, { - "test_results": [{"name": "fake_test", "category": "default", "status": f"{ProblemTestResultStatus.PASS.value}"}], - "eval_logs": "FAKE EVAL LOGS" - }) + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.finished, + { + "test_results": [ + {"name": "fake_test", "category": "default", "status": f"{ProblemTestResultStatus.PASS.value}"} + ], + "eval_logs": "FAKE EVAL LOGS", + }, + ) - - logger.info(f"Finished simulated evaluation run {evaluation_run_id} for problem {problem_name}") -async def _run_evaluation_run_with_semaphore(evaluation_run_id: UUID, problem_name: str, agent_code: str, semaphore: asyncio.Semaphore): +async def _run_evaluation_run_with_semaphore( + evaluation_run_id: UUID, problem_name: str, agent_code: str, semaphore: asyncio.Semaphore +): async with semaphore: return await _run_evaluation_run(evaluation_run_id, problem_name, agent_code) + # Run an evaluation run async def _run_evaluation_run(evaluation_run_id: UUID, problem_name: str, agent_code: str): try: @@ -161,21 +195,22 @@ async def _run_evaluation_run(evaluation_run_id: UUID, problem_name: str, agent_ # If we don't have a problem suite that supports this problem, mark the evaluation run as errored if problem_suite is None: - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.error, { - "error_code": EvaluationRunErrorCode.VALIDATOR_UNKNOWN_PROBLEM.value, - "error_message": f"The problem '{problem_name}' was not found in any problem suite" - }) + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.error, + { + "error_code": EvaluationRunErrorCode.VALIDATOR_UNKNOWN_PROBLEM.value, + "error_message": f"The problem '{problem_name}' was not found in any problem suite", + }, + ) return # Get the problem problem = problem_suite.get_problem(problem_name) - - logger.info(f"Starting evaluation run {evaluation_run_id} for problem {problem_name}...") - - try: # Move from pending -> initializing_agent await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.initializing_agent) @@ -188,25 +223,25 @@ async def _run_evaluation_run(evaluation_run_id: UUID, problem_name: str, agent_ evaluation_run_id, agent_code, running_agent_timeout_seconds, - include_solutions=config.INCLUDE_SOLUTIONS + include_solutions=config.INCLUDE_SOLUTIONS, ) # Move from initializing_agent -> running_agent await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.running_agent) # Start running the agent sandbox - patch, agent_logs = await asyncio.to_thread( - problem_suite.run_agent_sandbox, - sandbox_manager, - agent_sandbox + patch, agent_logs = await asyncio.to_thread(problem_suite.run_agent_sandbox, sandbox_manager, agent_sandbox) + logger.info( + f"Finished running agent for problem {problem_name}: {len(patch.splitlines())} lines of patch, {len(agent_logs.splitlines())} lines of agent logs" ) - logger.info(f"Finished running agent for problem {problem_name}: {len(patch.splitlines())} lines of patch, {len(agent_logs.splitlines())} lines of agent logs") # Move from running_agent -> initializing_eval - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.initializing_eval, { - "patch": patch, - "agent_logs": truncate_logs_if_required(agent_logs) - }) + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.initializing_eval, + {"patch": patch, "agent_logs": truncate_logs_if_required(agent_logs)}, + ) # Start initializing the evaluation sandbox eval_sandbox = await asyncio.to_thread( @@ -215,7 +250,7 @@ async def _run_evaluation_run(evaluation_run_id: UUID, problem_name: str, agent_ problem, evaluation_run_id, patch, - running_eval_timeout_seconds + running_eval_timeout_seconds, ) # Move from initializing_eval -> running_eval @@ -223,39 +258,51 @@ async def _run_evaluation_run(evaluation_run_id: UUID, problem_name: str, agent_ # Start running the evaluation sandbox test_results, eval_logs = await asyncio.to_thread( - problem_suite.run_eval_sandbox, - sandbox_manager, - eval_sandbox + problem_suite.run_eval_sandbox, sandbox_manager, eval_sandbox ) num_passed = sum(1 for test in test_results if test.status == ProblemTestResultStatus.PASS) num_failed = sum(1 for test in test_results if test.status == ProblemTestResultStatus.FAIL) num_skipped = sum(1 for test in test_results if test.status == ProblemTestResultStatus.SKIP) - logger.info(f"Finished running evaluation for problem {problem_name}: {len(test_results)} test results ({num_passed} passed, {num_failed} failed, {num_skipped} skipped), {len(eval_logs.splitlines())} lines of eval logs") + logger.info( + f"Finished running evaluation for problem {problem_name}: {len(test_results)} test results ({num_passed} passed, {num_failed} failed, {num_skipped} skipped), {len(eval_logs.splitlines())} lines of eval logs" + ) # Move from running_eval -> finished - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.finished, { - "test_results": [test.model_dump() for test in test_results], - "eval_logs": truncate_logs_if_required(eval_logs) - }) + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.finished, + { + "test_results": [test.model_dump() for test in test_results], + "eval_logs": truncate_logs_if_required(eval_logs), + }, + ) except EvaluationRunException as e: logger.error(f"Evaluation run {evaluation_run_id} for problem {problem_name} errored: {e}") - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.error, { - "error_code": e.error_code.value, - "error_message": e.error_message - }) + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.error, + {"error_code": e.error_code.value, "error_message": e.error_message}, + ) except Exception as e: - logger.error(f"Evaluation run {evaluation_run_id} for problem {problem_name} errored: {EvaluationRunErrorCode.VALIDATOR_INTERNAL_ERROR.get_error_message()}: {e}") + logger.error( + f"Evaluation run {evaluation_run_id} for problem {problem_name} errored: {EvaluationRunErrorCode.VALIDATOR_INTERNAL_ERROR.get_error_message()}: {e}" + ) logger.error(traceback.format_exc()) - await update_evaluation_run(evaluation_run_id, problem_name, EvaluationRunStatus.error, { - "error_code": EvaluationRunErrorCode.VALIDATOR_INTERNAL_ERROR.value, - "error_message": f"{EvaluationRunErrorCode.VALIDATOR_INTERNAL_ERROR.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}" - }) - - + await update_evaluation_run( + evaluation_run_id, + problem_name, + EvaluationRunStatus.error, + { + "error_code": EvaluationRunErrorCode.VALIDATOR_INTERNAL_ERROR.value, + "error_message": f"{EvaluationRunErrorCode.VALIDATOR_INTERNAL_ERROR.get_error_message()}: {e}\n\nTraceback:\n{traceback.format_exc()}", + }, + ) logger.info(f"Finished evaluation run {evaluation_run_id} for problem {problem_name}") @@ -263,7 +310,6 @@ async def _run_evaluation_run(evaluation_run_id: UUID, problem_name: str, agent_ logger.error(f"Error in _run_evaluation_run(): {type(e).__name__}: {e}") logger.error(traceback.format_exc()) os._exit(1) - # Run an evaluation, automatically dispatches all runs to either _simulate_run_evaluation_run or _run_evaluation_run @@ -274,8 +320,6 @@ async def _run_evaluation(request_evaluation_response: ValidatorRequestEvaluatio for evaluation_run in request_evaluation_response.evaluation_runs: logger.info(f" {evaluation_run.problem_name}") - - logger.info("Starting evaluation...") tasks = [] @@ -285,16 +329,27 @@ async def _run_evaluation(request_evaluation_response: ValidatorRequestEvaluatio problem_name = evaluation_run.problem_name if config.SIMULATE_EVALUATION_RUNS: - tasks.append(asyncio.create_task(_simulate_run_evaluation_run_with_semaphore(evaluation_run_id, problem_name, semaphore))) + tasks.append( + asyncio.create_task( + _simulate_run_evaluation_run_with_semaphore(evaluation_run_id, problem_name, semaphore) + ) + ) else: - tasks.append(asyncio.create_task(_run_evaluation_run_with_semaphore(evaluation_run_id, problem_name, request_evaluation_response.agent_code, semaphore))) + tasks.append( + asyncio.create_task( + _run_evaluation_run_with_semaphore( + evaluation_run_id, problem_name, request_evaluation_response.agent_code, semaphore + ) + ) + ) await asyncio.gather(*tasks) logger.info("Finished evaluation") - await post_ridges_platform("/validator/finish-evaluation", ValidatorFinishEvaluationRequest(), bearer_token=session_id, quiet=1) - + await post_ridges_platform( + "/validator/finish-evaluation", ValidatorFinishEvaluationRequest(), bearer_token=session_id, quiet=1 + ) # Main loop @@ -306,8 +361,6 @@ async def main(): global sandbox_manager global problem_suites - - # Register with the Ridges platform, yielding us a session ID logger.info("Registering validator...") @@ -316,21 +369,33 @@ async def main(): # Get the current timestamp, and sign it with the validator hotkey timestamp = int(time.time()) signed_timestamp = config.VALIDATOR_HOTKEY.sign(str(timestamp)).hex() - - register_response = ValidatorRegistrationResponse(**(await post_ridges_platform("/validator/register-as-validator", ValidatorRegistrationRequest( - timestamp=timestamp, - signed_timestamp=signed_timestamp, - hotkey=config.VALIDATOR_HOTKEY.ss58_address, - commit_hash=COMMIT_HASH - )))) - + + register_response = ValidatorRegistrationResponse( + **( + await post_ridges_platform( + "/validator/register-as-validator", + ValidatorRegistrationRequest( + timestamp=timestamp, + signed_timestamp=signed_timestamp, + hotkey=config.VALIDATOR_HOTKEY.ss58_address, + commit_hash=COMMIT_HASH, + ), + ) + ) + ) + elif config.MODE == "screener": - register_response = ScreenerRegistrationResponse(**(await post_ridges_platform("/validator/register-as-screener", ScreenerRegistrationRequest( - name=config.SCREENER_NAME, - password=config.SCREENER_PASSWORD, - commit_hash=COMMIT_HASH - )))) - + register_response = ScreenerRegistrationResponse( + **( + await post_ridges_platform( + "/validator/register-as-screener", + ScreenerRegistrationRequest( + name=config.SCREENER_NAME, password=config.SCREENER_PASSWORD, commit_hash=COMMIT_HASH + ), + ) + ) + ) + except httpx.HTTPStatusError as e: if config.UPDATE_AUTOMATICALLY and e.response.status_code == 426: logger.info("Updating...") @@ -338,7 +403,7 @@ async def main(): sys.exit(0) else: raise e - + session_id = register_response.session_id running_agent_timeout_seconds = register_response.running_agent_timeout_seconds running_eval_timeout_seconds = register_response.running_eval_timeout_seconds @@ -350,26 +415,20 @@ async def main(): logger.info(f" Running Evaluation Timeout: {running_eval_timeout_seconds} second(s)") logger.info(f" Max Evaluation Run Log Size: {max_evaluation_run_log_size_bytes} byte(s)") - - # Create the sandbox manager sandbox_manager = SandboxManager(config.RIDGES_INFERENCE_GATEWAY_URL) # Load all problem suites problem_suites = [POLYGLOT_PY_SUITE, POLYGLOT_JS_SUITE, SWEBENCH_VERIFIED_SUITE] - - # Get all the problems in the latest set latest_set_problems_data = await get_ridges_platform("/evaluation-sets/all-latest-set-problems", quiet=1) latest_set_problems = [EvaluationSetProblem(**prob) for prob in latest_set_problems_data] latest_set_problem_names = list({prob.problem_name for prob in latest_set_problems}) - + # Prebuild the images for the SWE-Bench Verified problems SWEBENCH_VERIFIED_SUITE.prebuild_problem_images(latest_set_problem_names) - - # Start the send heartbeat loop asyncio.create_task(send_heartbeat_loop()) @@ -377,24 +436,25 @@ async def main(): # Start the set weights loop asyncio.create_task(set_weights_loop()) - - # Loop forever, just keep requesting evaluations and running them while True: logger.info("Requesting an evaluation...") - - request_evaluation_response_data = await post_ridges_platform("/validator/request-evaluation", ValidatorRequestEvaluationRequest(), bearer_token=session_id, quiet=1) + + request_evaluation_response_data = await post_ridges_platform( + "/validator/request-evaluation", ValidatorRequestEvaluationRequest(), bearer_token=session_id, quiet=1 + ) # If no evaluation is available, wait and try again if request_evaluation_response_data is None: - logger.info(f"No evaluations available. Waiting for {config.REQUEST_EVALUATION_INTERVAL_SECONDS} seconds...") + logger.info( + f"No evaluations available. Waiting for {config.REQUEST_EVALUATION_INTERVAL_SECONDS} seconds..." + ) await asyncio.sleep(config.REQUEST_EVALUATION_INTERVAL_SECONDS) continue await _run_evaluation(ValidatorRequestEvaluationResponse(**request_evaluation_response_data)) - if __name__ == "__main__": try: asyncio.run(main()) diff --git a/validator/set_weights.py b/validator/set_weights.py index 2ab18c937..542cc6b37 100644 --- a/validator/set_weights.py +++ b/validator/set_weights.py @@ -1,17 +1,14 @@ # NOTE ADAM: Subtensor bug (self.disable_third_party_loggers()) +from typing import Dict + from bittensor.core.async_subtensor import AsyncSubtensor import utils.logger as logger import validator.config as config -from typing import Dict - - - subtensor = AsyncSubtensor(network=config.SUBTENSOR_NETWORK, fallback_endpoints=[config.SUBTENSOR_ADDRESS]) - async def set_weights_from_mapping(weights_mapping: Dict[str, float]) -> None: if len(weights_mapping.keys()) != 1: logger.error("Expected one hotkey") @@ -22,7 +19,9 @@ async def set_weights_from_mapping(weights_mapping: Dict[str, float]) -> None: logger.error("Expected weight of 1") return - weight_receiving_uid = await subtensor.get_uid_for_hotkey_on_subnet(hotkey_ss58=weight_receiving_hotkey, netuid=config.NETUID) + weight_receiving_uid = await subtensor.get_uid_for_hotkey_on_subnet( + hotkey_ss58=weight_receiving_hotkey, netuid=config.NETUID + ) if weight_receiving_uid is None: logger.error(f"Weight receiving hotkey {weight_receiving_hotkey} not found") return @@ -35,10 +34,10 @@ async def set_weights_from_mapping(weights_mapping: Dict[str, float]) -> None: uids=[weight_receiving_uid], weights=[1], wait_for_inclusion=True, - wait_for_finalization=True + wait_for_finalization=True, ) if success: logger.info(f"Set weight of hotkey {weight_receiving_hotkey} to 1") else: - logger.error(f"Failed to set weight of hotkey {weight_receiving_hotkey} to 1: {message}") \ No newline at end of file + logger.error(f"Failed to set weight of hotkey {weight_receiving_hotkey} to 1: {message}")