From 055e4e6487eecdd56148a42203bdbef932d5fe3f Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 6 Nov 2025 21:04:03 -0500 Subject: [PATCH 01/20] Update software-agent-sdk submodule to main --- vendor/software-agent-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/software-agent-sdk b/vendor/software-agent-sdk index a612c0a6..84fe9f07 160000 --- a/vendor/software-agent-sdk +++ b/vendor/software-agent-sdk @@ -1 +1 @@ -Subproject commit a612c0a685fa96bc725085ac81c59492d4a88974 +Subproject commit 84fe9f07742a29ca435c9736caa3cf00ca76f64e From a6ec978d85ecc0c0f748bce3117f5b91d3a1a1ff Mon Sep 17 00:00:00 2001 From: openhands Date: Fri, 7 Nov 2025 12:51:07 -0500 Subject: [PATCH 02/20] initial commit, eval for code search --- benchmarks/agentic_code_search/__init__.py | 0 .../agentic_code_search/prompts/default.j2 | 11 + benchmarks/agentic_code_search/run_infer.py | 340 ++++++++++++++++++ benchmarks/utils/evaluation.py | 8 +- 4 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 benchmarks/agentic_code_search/__init__.py create mode 100644 benchmarks/agentic_code_search/prompts/default.j2 create mode 100644 benchmarks/agentic_code_search/run_infer.py diff --git a/benchmarks/agentic_code_search/__init__.py b/benchmarks/agentic_code_search/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/benchmarks/agentic_code_search/prompts/default.j2 b/benchmarks/agentic_code_search/prompts/default.j2 new file mode 100644 index 00000000..1fe7a6eb --- /dev/null +++ b/benchmarks/agentic_code_search/prompts/default.j2 @@ -0,0 +1,11 @@ +I have access to a python code repository in the directory {{ working_dir }} . + +Consider the following issue description: + + +{{ problem_statement }} + + +Act as a code search agent to find the relevant files where we need to make changes so that is met? +NOTE: You do not need to solve the issue, all you need to do is find the relevant files. Your output will be used to guide another agent to solve the issue. +Only use grep to find and please output your answer as just a list of the relevant files that are directly related to the in the format ['file1', 'file2', ...] using the message parameter of the Finish tool. \ No newline at end of file diff --git a/benchmarks/agentic_code_search/run_infer.py b/benchmarks/agentic_code_search/run_infer.py new file mode 100644 index 00000000..d5d03172 --- /dev/null +++ b/benchmarks/agentic_code_search/run_infer.py @@ -0,0 +1,340 @@ +import ast +import os +from argparse import ArgumentParser +from pathlib import Path +from typing import List + +import pandas as pd +from jinja2 import Environment, FileSystemLoader + +# from benchmarks.utils.args_parser import get_parser +from benchmarks.utils.evaluation import Evaluation +from benchmarks.utils.evaluation_utils import ( + construct_eval_output_dir, + get_default_on_result_writer, +) +from benchmarks.utils.models import ( + EvalInstance, + EvalMetadata, + EvalOutput, +) +from openhands.sdk import LLM, Agent, Conversation, get_logger +from openhands.sdk.conversation import get_agent_final_response +from openhands.sdk.workspace import LocalWorkspace, RemoteWorkspace +from openhands.tools.preset.default import get_default_tools +from openhands.workspace import DockerWorkspace + + +logger = get_logger(__name__) + + +def get_parser(): + """Create and return argument parser. + + Returns: + ArgumentParser instance + """ + prompt_dir = (Path(__file__).parent / "prompts").resolve() + default_prompt_path = prompt_dir / "default.j2" + assert default_prompt_path.exists(), ( + f"Default prompt path file {default_prompt_path} not found" + ) + parser = ArgumentParser(description="Run inference on code localization dataset") + parser.add_argument( + "--dataset_file", + type=str, + required=True, + help="Path of the prepared dataset parquet file.", + ) + parser.add_argument("--split", type=str, default="test", help="Dataset split") + parser.add_argument( + "--llm-config-path", + type=str, + required=True, + help="Path to JSON LLM configuration", + ) + parser.add_argument( + "--max-iterations", + type=int, + default=25, + help="Maximum steps allowed for the agent", + ) + parser.add_argument( + "--num-workers", type=int, default=1, help="Number of evaluation workers" + ) + # parser.add_argument("--note", type=str, default="initial", help="Evaluation note") + parser.add_argument( + "--output-dir", + type=str, + default="./evaluation_outputs", + help="Evaluation output directory", + ) + parser.add_argument( + "--n-limit", + type=int, + default=-1, + help="Limit number of instances to evaluate", + ) + parser.add_argument( + "--prompt-path", + type=str, + default=str(default_prompt_path), + help="Path to prompt template file", + ) + # parser.add_argument( + # "--num_runs", + # type=int, + # default=1, + # help="Number of evaluation runs for each task instance (useful for best@K evaluation)" + # ) + parser.add_argument( + "--runtime", + type=str, + default="local", + choices=["local", "docker"], + help="Runtime environment for the agent (local or docker)", + ) + parser.add_argument( + "--select", + type=str, + default="", + help="Path to text file containing instance IDs to select (one per line)", + ) + parser.add_argument( + "--workspace_base_dir", + type=str, + default="/tmp/workspace/", + help="Base directory for local workspaces (ignored for remote workspaces)", + ) + return parser + + +def get_instruction(instance: EvalInstance, metadata: EvalMetadata) -> str: + working_dir = instance.data["repo_dir"] + problem_statement = instance.data["problem_statement"] + if metadata.prompt_path is not None: + prompts_dir = os.path.dirname(metadata.prompt_path) + template_name = os.path.basename(metadata.prompt_path) + env = Environment(loader=FileSystemLoader(prompts_dir)) + template = env.get_template(template_name) + context = {"problem_statement": problem_statement, "working_dir": working_dir} + instruction = template.render(context) + return instruction + else: + raise ValueError("metadata.prompt_path is None") + + +def f1_reward_function(predicted_files, true_files): + pred, true = set(predicted_files), set(true_files) + tp = len(pred & true) + precision = tp / len(pred) if pred else 0.0 + recall = tp / len(true) if true else 0.0 + if not pred and not true: + return 1.0 + return ( + 0.0 + if precision + recall == 0 + else 2 * precision * recall / (precision + recall) + ) + + +def reward_function( + final_message: str, instance: dict +) -> tuple[float, set[str], set[str]]: + true_files = set(x[0] for x in ast.literal_eval(instance["target"])) + score = 0.0 + try: + predicted_files = set( + ast.literal_eval( + final_message.split("")[1].split("")[0] + ) + ) + score = f1_reward_function(predicted_files, true_files) + except Exception as e: + print(f"Error parsing final message: {e}") + return 0.0, set(), true_files + + return score, predicted_files, true_files + + +class AgenticCodeSearchEvaluation(Evaluation): + def prepare_instances(self) -> List[EvalInstance]: + logger.info("Setting up agentic code search evaluation.") + + # Load dataset + dataset = pd.read_parquet(self.metadata.dataset) + + instances: List[EvalInstance] = [] + for _, row in dataset.iterrows(): + inst_id = str(row["instance_id"]) + instances.append(EvalInstance(id=inst_id, data=row.to_dict())) + + logger.info("Total instances to process: %d", len(instances)) + return instances + + def prepare_workspace(self, instance: EvalInstance): + runtime_type = ( + self.metadata.details.get("runtime", "local") + if isinstance(self.metadata.details, dict) + else "local" + ) + repo_name = instance.data["repo"] + if runtime_type == "local": + assert isinstance(self.metadata.details, dict) + working_dir = Path(self.metadata.details["workspace_base_dir"]).resolve() + # instance_dir_name = f"{repo_name.replace('/', '_')}_{instance_id}" + # working_dir = output_dir / instance_dir_name + workspace = LocalWorkspace(working_dir=str(working_dir)) + repo_dir = working_dir / instance.id + repo_dir = str(repo_dir) + elif runtime_type == "docker": + # TODO: directly use prebuilt agent-server image? + workspace = DockerWorkspace( + base_image="nikolaik/python-nodejs:python3.12-nodejs22", + working_dir="/workspace", + ) + repo_dir = f"/workspace/{repo_name.split('/')[-1]}/" + else: + raise NotImplementedError(f"Unsupported runtime type: {runtime_type}") + + base_commit_id = instance.data["base_commit"] + instance.data["repo_dir"] = ( + repo_dir # pass repo_dir to instance data for later use + ) + + # run environment setup commands for cloning repo + # remove working dir if it already exists + rm_dir = workspace.execute_command(f"rm -rf {repo_dir}") + assert rm_dir.exit_code == 0, ( + f"Failed to remove existing working dir: {rm_dir.stderr}" + ) + + # make new empty directory + mk_dir = workspace.execute_command(f"mkdir -p {repo_dir}") + assert mk_dir.exit_code == 0, ( + f"Failed to create repo directory: {mk_dir.stderr}" + ) + + # clone repo inside repo_dir + repo_url = f"https://github.com/{repo_name}.git" + clone_repo = workspace.execute_command(f"git clone {repo_url} {repo_dir}") + assert clone_repo.exit_code == 0, f"Failed to clone repo: {clone_repo.stderr}" + + # checkout to base commit + checkout_commit = workspace.execute_command( + f"git -C {repo_dir} checkout {base_commit_id}" + ) + assert checkout_commit.exit_code == 0, ( + f"Failed to checkout to commit {base_commit_id}: {checkout_commit.stderr}" + ) + + logger.info(f"Prepared workspace successfully for instance {instance.id}") + return workspace + + def evaluate_instance(self, instance, workspace): + """ + Steps: + 1. Prepare the prompt using Jinja2 template + 2. Create agent with the prompt + 3. Run the agent in a conversation until max iterations or task completion + 4. Collect and return the output + """ + instruction = get_instruction(instance, self.metadata) + # NOTE: the default condenser is LLM-based summarizer in get_default_agent, disabling it for now as done in SWE-Bench. This is why we make agent manually here. + tools = get_default_tools(enable_browser=False) + agent = Agent( + llm=self.metadata.llm, + tools=tools, + system_prompt_kwargs={"cli_mode": True}, + ) + + def _log_event(ev): # keep it simple + logger.debug("Event: %s", ev) + + assert isinstance(workspace, RemoteWorkspace) or isinstance( + workspace, LocalWorkspace + ) + conversation = Conversation( + agent=agent, + workspace=workspace, + callbacks=[_log_event], + max_iteration_per_run=self.metadata.max_iterations, + ) + conversation.send_message(instruction) + conversation.run() + history = list(map(lambda event: event.model_dump(), conversation.state.events)) + finish_message = get_agent_final_response(conversation.state.events) + reward, predicted_files, true_files = reward_function( + finish_message, instance.data + ) + out = EvalOutput( + instance_id=instance.id, + test_result={ + "reward": reward, + "predicted_files": list(predicted_files), + "true_files": list(true_files), + }, + instruction=instruction, + error=None, + history=history, + metrics=conversation.conversation_stats.get_combined_metrics(), + ) + return out + + +def main(): + parser = get_parser() + args = parser.parse_args() + + # load LLM configuration + llm_config_path = args.llm_config_path + if not os.path.isfile(llm_config_path): + raise ValueError(f"LLM config file {llm_config_path} does not exist") + with open(llm_config_path, "r") as f: + llm_config = f.read() + llm = LLM.model_validate_json(llm_config) + logger.info("Using LLM config: %s", llm.model_dump_json(indent=2)) + + # Load LLM configuration + with open(args.llm_config_path, "r") as f: + llm_config = f.read() + llm = LLM.model_validate_json(llm_config) + logger.info("Using LLM config: %s", llm.model_dump_json(indent=2)) + + structured_output_dir = construct_eval_output_dir( + base_dir=args.output_dir, + dataset_name=f"agentic_code_search_{args.dataset_file.split('.parquet')[0]}", + model_name=llm.model, + max_iterations=args.max_iterations, + eval_note="", + ) + + metadata = EvalMetadata( + llm=llm, + dataset=args.dataset_file, + dataset_split=args.split, + max_iterations=args.max_iterations, + eval_output_dir=structured_output_dir, + details={ + "runtime": args.runtime, + "workspace_base_dir": args.workspace_base_dir, + }, + prompt_path=args.prompt_path, + eval_limit=args.n_limit, + env_setup_commands=[], + # max_attempts=args.max_attempts, + # critic_name=args.critic, + selected_instances_file=args.select if args.select else None, + # max_retries=args.max_retries, + ) + + evaluator = AgenticCodeSearchEvaluation( + metadata=metadata, num_workers=args.num_workers + ) + evaluator.run(on_result=get_default_on_result_writer(evaluator.output_path)) + + logger.info("Evaluation completed!") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/utils/evaluation.py b/benchmarks/utils/evaluation.py index cf862a37..5fd56c1d 100644 --- a/benchmarks/utils/evaluation.py +++ b/benchmarks/utils/evaluation.py @@ -21,7 +21,7 @@ EvalOutput, ) from openhands.sdk import get_logger -from openhands.sdk.workspace import RemoteWorkspace +from openhands.sdk.workspace import LocalWorkspace, RemoteWorkspace logger = get_logger(__name__) @@ -59,13 +59,15 @@ def prepare_instances(self) -> List[EvalInstance]: raise NotImplementedError @abstractmethod - def prepare_workspace(self, instance: EvalInstance) -> RemoteWorkspace: + def prepare_workspace( + self, instance: EvalInstance + ) -> RemoteWorkspace | LocalWorkspace: """Create and return a context-managed Workspace for the given instance.""" raise NotImplementedError @abstractmethod def evaluate_instance( - self, instance: EvalInstance, workspace: RemoteWorkspace + self, instance: EvalInstance, workspace: RemoteWorkspace | LocalWorkspace ) -> EvalOutput: """Run evaluation for a single instance in the provided workspace.""" raise NotImplementedError From 36fa26758afe54a5d94c4eddde8b9f3c7e6eaabd Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Fri, 7 Nov 2025 12:53:52 -0500 Subject: [PATCH 03/20] Num runs should be managed by the user externally --- benchmarks/agentic_code_search/run_infer.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/benchmarks/agentic_code_search/run_infer.py b/benchmarks/agentic_code_search/run_infer.py index d5d03172..43d4bdce 100644 --- a/benchmarks/agentic_code_search/run_infer.py +++ b/benchmarks/agentic_code_search/run_infer.py @@ -81,12 +81,7 @@ def get_parser(): default=str(default_prompt_path), help="Path to prompt template file", ) - # parser.add_argument( - # "--num_runs", - # type=int, - # default=1, - # help="Number of evaluation runs for each task instance (useful for best@K evaluation)" - # ) + parser.add_argument( "--runtime", type=str, From 7d3d3603d4e315aad0a9dd543e922f441e40532b Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Fri, 7 Nov 2025 21:07:40 +0000 Subject: [PATCH 04/20] Update software-agent-sdk submodule to main --- vendor/software-agent-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/software-agent-sdk b/vendor/software-agent-sdk index 84fe9f07..9652de4c 160000 --- a/vendor/software-agent-sdk +++ b/vendor/software-agent-sdk @@ -1 +1 @@ -Subproject commit 84fe9f07742a29ca435c9736caa3cf00ca76f64e +Subproject commit 9652de4c5acda76780cca0812eff21e0d8ce9c9d From 5bf46dd858d614c06461ea8dbf109b1a9dfac55e Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Fri, 7 Nov 2025 23:05:08 +0000 Subject: [PATCH 05/20] docker works --- benchmarks/agentic_code_search/run_infer.py | 38 ++++++++++++++------- benchmarks/agentic_code_search/run_infer.sh | 8 +++++ 2 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 benchmarks/agentic_code_search/run_infer.sh diff --git a/benchmarks/agentic_code_search/run_infer.py b/benchmarks/agentic_code_search/run_infer.py index 43d4bdce..94eb86d9 100644 --- a/benchmarks/agentic_code_search/run_infer.py +++ b/benchmarks/agentic_code_search/run_infer.py @@ -7,6 +7,8 @@ import pandas as pd from jinja2 import Environment, FileSystemLoader +from benchmarks.utils.dataset import prepare_dataset + # from benchmarks.utils.args_parser import get_parser from benchmarks.utils.evaluation import Evaluation from benchmarks.utils.evaluation_utils import ( @@ -138,18 +140,28 @@ def reward_function( ) -> tuple[float, set[str], set[str]]: true_files = set(x[0] for x in ast.literal_eval(instance["target"])) score = 0.0 + repo_dir = str(instance["repo_dir"]) + if not repo_dir.endswith("/"): + repo_dir += "/" try: predicted_files = set( ast.literal_eval( final_message.split("")[1].split("")[0] ) ) - score = f1_reward_function(predicted_files, true_files) + relative_predicted_files = set() + for file_path in predicted_files: + if file_path.startswith(repo_dir): + relative_path = file_path[len(repo_dir) :] + else: + relative_path = file_path + relative_predicted_files.add(relative_path) + score = f1_reward_function(relative_predicted_files, true_files) except Exception as e: print(f"Error parsing final message: {e}") return 0.0, set(), true_files - return score, predicted_files, true_files + return score, relative_predicted_files, true_files class AgenticCodeSearchEvaluation(Evaluation): @@ -158,6 +170,9 @@ def prepare_instances(self) -> List[EvalInstance]: # Load dataset dataset = pd.read_parquet(self.metadata.dataset) + dataset = prepare_dataset( + dataset, self.metadata.eval_limit, self.metadata.selected_instances_file + ) instances: List[EvalInstance] = [] for _, row in dataset.iterrows(): @@ -185,8 +200,9 @@ def prepare_workspace(self, instance: EvalInstance): elif runtime_type == "docker": # TODO: directly use prebuilt agent-server image? workspace = DockerWorkspace( - base_image="nikolaik/python-nodejs:python3.12-nodejs22", + # base_image="nikolaik/python-nodejs:python3.12-nodejs22", working_dir="/workspace", + server_image="ghcr.io/openhands/agent-server:latest-python", ) repo_dir = f"/workspace/{repo_name.split('/')[-1]}/" else: @@ -212,12 +228,14 @@ def prepare_workspace(self, instance: EvalInstance): # clone repo inside repo_dir repo_url = f"https://github.com/{repo_name}.git" - clone_repo = workspace.execute_command(f"git clone {repo_url} {repo_dir}") + clone_repo = workspace.execute_command( + f"git clone {repo_url} {repo_dir}", timeout=10 * 60 + ) assert clone_repo.exit_code == 0, f"Failed to clone repo: {clone_repo.stderr}" # checkout to base commit checkout_commit = workspace.execute_command( - f"git -C {repo_dir} checkout {base_commit_id}" + f"git -C {repo_dir} checkout {base_commit_id}", timeout=5 * 60 ) assert checkout_commit.exit_code == 0, ( f"Failed to checkout to commit {base_commit_id}: {checkout_commit.stderr}" @@ -259,6 +277,8 @@ def _log_event(ev): # keep it simple conversation.run() history = list(map(lambda event: event.model_dump(), conversation.state.events)) finish_message = get_agent_final_response(conversation.state.events) + if finish_message == "": + logger.info("No final response from agent.") reward, predicted_files, true_files = reward_function( finish_message, instance.data ) @@ -290,15 +310,9 @@ def main(): llm = LLM.model_validate_json(llm_config) logger.info("Using LLM config: %s", llm.model_dump_json(indent=2)) - # Load LLM configuration - with open(args.llm_config_path, "r") as f: - llm_config = f.read() - llm = LLM.model_validate_json(llm_config) - logger.info("Using LLM config: %s", llm.model_dump_json(indent=2)) - structured_output_dir = construct_eval_output_dir( base_dir=args.output_dir, - dataset_name=f"agentic_code_search_{args.dataset_file.split('.parquet')[0]}", + dataset_name=f"agentic_code_search_{args.dataset_file.split('/')[-1].split('.parquet')[0]}", model_name=llm.model, max_iterations=args.max_iterations, eval_note="", diff --git a/benchmarks/agentic_code_search/run_infer.sh b/benchmarks/agentic_code_search/run_infer.sh new file mode 100644 index 00000000..dbb59e75 --- /dev/null +++ b/benchmarks/agentic_code_search/run_infer.sh @@ -0,0 +1,8 @@ +uv run python -m benchmarks.agentic_code_search.run_infer \ + --dataset_file train.parquet \ + --llm-config-path .llm_config/qwen3.json \ + --max-iterations 25 \ + --num-workers 8 \ + --output-dir ./docker_outputs \ + --n-limit 10 \ + --runtime docker \ No newline at end of file From 1fc3cac6c2fa9811aaecae887adb14ec21c9f422 Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Fri, 7 Nov 2025 23:12:24 +0000 Subject: [PATCH 06/20] example config for qwen3 --- .llm_config/qwen3.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .llm_config/qwen3.json diff --git a/.llm_config/qwen3.json b/.llm_config/qwen3.json new file mode 100644 index 00000000..bb106174 --- /dev/null +++ b/.llm_config/qwen3.json @@ -0,0 +1,8 @@ +{ + "model": "openai/Qwen/Qwen3-8B", + "base_url": "http://172.17.0.1:2339/v1", + "api_key": "aditya_vllm", + "temperature": 0.6, + "top_p": 0.95, + "top_k": 20 +} From 5f74f63da1147499ff07bbfb089b8591f5ce6bec Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Fri, 7 Nov 2025 23:32:02 +0000 Subject: [PATCH 07/20] local runtime works --- benchmarks/agentic_code_search/run_infer.py | 6 ++++++ benchmarks/agentic_code_search/run_infer.sh | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/benchmarks/agentic_code_search/run_infer.py b/benchmarks/agentic_code_search/run_infer.py index 94eb86d9..48888887 100644 --- a/benchmarks/agentic_code_search/run_infer.py +++ b/benchmarks/agentic_code_search/run_infer.py @@ -282,6 +282,12 @@ def _log_event(ev): # keep it simple reward, predicted_files, true_files = reward_function( finish_message, instance.data ) + if ( + self.metadata.details is not None + and self.metadata.details["runtime"] == "local" + ): + # clean up workspace after use + workspace.execute_command(f"rm -rf {instance.data['repo_dir']}") out = EvalOutput( instance_id=instance.id, test_result={ diff --git a/benchmarks/agentic_code_search/run_infer.sh b/benchmarks/agentic_code_search/run_infer.sh index dbb59e75..de224048 100644 --- a/benchmarks/agentic_code_search/run_infer.sh +++ b/benchmarks/agentic_code_search/run_infer.sh @@ -5,4 +5,14 @@ uv run python -m benchmarks.agentic_code_search.run_infer \ --num-workers 8 \ --output-dir ./docker_outputs \ --n-limit 10 \ - --runtime docker \ No newline at end of file + --runtime docker + +uv run python -m benchmarks.agentic_code_search.run_infer \ + --dataset_file train.parquet \ + --llm-config-path .llm_config/qwen3.json \ + --max-iterations 25 \ + --num-workers 1 \ + --output-dir ./local_outputs \ + --n-limit 1 \ + --workspace_base_dir /tmp/workspace/ \ + --runtime local \ No newline at end of file From 5e2820d7b93512ab918c42897912e659bd0e9ae8 Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Sat, 8 Nov 2025 19:49:43 -0500 Subject: [PATCH 08/20] use host network in agent sdk --- vendor/software-agent-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/software-agent-sdk b/vendor/software-agent-sdk index 9652de4c..572ea214 160000 --- a/vendor/software-agent-sdk +++ b/vendor/software-agent-sdk @@ -1 +1 @@ -Subproject commit 9652de4c5acda76780cca0812eff21e0d8ce9c9d +Subproject commit 572ea2148d75c1ed472b74e233bc0f476df03fd3 From bfe182a8a02086358d71d8b1365c8ed2565c1cca Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Mon, 10 Nov 2025 11:51:22 -0500 Subject: [PATCH 09/20] add eval --- .gitignore | 2 ++ .pre-commit-config.yaml | 8 ------- benchmarks/agentic_code_search/eval_infer.py | 23 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 benchmarks/agentic_code_search/eval_infer.py diff --git a/.gitignore b/.gitignore index d89f1a36..0e4aba8f 100644 --- a/.gitignore +++ b/.gitignore @@ -217,3 +217,5 @@ workspace/ # Evaluation outputs ./eval_outputs ./builds +swe_bench_lite.parquet +./docker_outputs/ \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40ae9a33..2b085fe0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,11 +25,3 @@ repos: types: [python] pass_filenames: true always_run: false - - id: pyright - name: Type check with Pyright (strict) - entry: uv - args: [run, pyright] - language: system - types: [python] - pass_filenames: true - always_run: false diff --git a/benchmarks/agentic_code_search/eval_infer.py b/benchmarks/agentic_code_search/eval_infer.py new file mode 100644 index 00000000..d46c1c16 --- /dev/null +++ b/benchmarks/agentic_code_search/eval_infer.py @@ -0,0 +1,23 @@ +import json +from argparse import ArgumentParser + + +def main(args): + results_file = args.results_file + f1_total = 0 + cnt = 0 + with open(results_file, "r") as f: + for line in f: + result = json.loads(line) + print(result["test_result"]["reward"]) + if result["test_result"]["reward"] is not None: + f1_total += result["test_result"]["reward"] + cnt += 1 + print(f"Average F1 score: {f1_total / cnt:.4f} over {cnt} samples") + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("--results_file", type=str, required=True) + args = parser.parse_args() + main(args) From 72ef6ff08e74866cc234ee2838619d507fc9026f Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Mon, 10 Nov 2025 11:51:54 -0500 Subject: [PATCH 10/20] add eval --- .pre-commit-config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2b085fe0..40ae9a33 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,3 +25,11 @@ repos: types: [python] pass_filenames: true always_run: false + - id: pyright + name: Type check with Pyright (strict) + entry: uv + args: [run, pyright] + language: system + types: [python] + pass_filenames: true + always_run: false From b8911494e4d70a6e3a05ce2fa12649db86e4747e Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Mon, 10 Nov 2025 12:13:03 -0500 Subject: [PATCH 11/20] add analysis code --- .pre-commit-config.yaml | 16 +- .../agentic_code_search/analyse_tool_calls.py | 48 ++++ pyproject.toml | 1 + tool_call_qwen3_4b.png | Bin 0 -> 15378 bytes tool_call_qwen3_8b.png | Bin 0 -> 14720 bytes uv.lock | 244 ++++++++++++++++++ 6 files changed, 301 insertions(+), 8 deletions(-) create mode 100644 benchmarks/agentic_code_search/analyse_tool_calls.py create mode 100644 tool_call_qwen3_4b.png create mode 100644 tool_call_qwen3_8b.png diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40ae9a33..d435cfc6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,11 +25,11 @@ repos: types: [python] pass_filenames: true always_run: false - - id: pyright - name: Type check with Pyright (strict) - entry: uv - args: [run, pyright] - language: system - types: [python] - pass_filenames: true - always_run: false + # - id: pyright + # name: Type check with Pyright (strict) + # entry: uv + # args: [run, pyright] + # language: system + # types: [python] + # pass_filenames: true + # always_run: false diff --git a/benchmarks/agentic_code_search/analyse_tool_calls.py b/benchmarks/agentic_code_search/analyse_tool_calls.py new file mode 100644 index 00000000..3cca2d60 --- /dev/null +++ b/benchmarks/agentic_code_search/analyse_tool_calls.py @@ -0,0 +1,48 @@ +import json +from argparse import ArgumentParser + +import matplotlib.pyplot as plt + + +def main(args): + results_file = args.results_file + tool_call_counts = {} + tot = 0 + with open(results_file, "r") as f: + for line in f: + trajectory = json.loads(line) + cnt = 0 + for event in trajectory.get("history", []): + if event["kind"] == "ActionEvent" and event["source"] == "agent": + cnt += 1 + if cnt == 0: + continue + tool_call_counts[cnt] = tool_call_counts.get(cnt, 0) + 1 + tot += 1 + print(tool_call_counts) + for k, v in tool_call_counts.items(): + tool_call_counts[k] = (v / tot) * 100 + # plot histogram + plt.bar(list(tool_call_counts.keys()), list(tool_call_counts.values())) + plt.xlabel("Number of Tool Calls") + plt.ylabel("Percentage of Instances (%)") + plt.savefig(args.output_image) + plt.close() + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument( + "--results_file", + type=str, + required=True, + help="Path to the JSONL file containing eventstream data.", + ) + parser.add_argument( + "--output_image", + type=str, + default="tool_call_histogram.png", + help="Path to save the output histogram image.", + ) + args = parser.parse_args() + main(args) diff --git a/pyproject.toml b/pyproject.toml index 5e924d58..6b936277 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ dependencies = [ "openhands-workspace", "modal>=1.1.4", "swebench", + "matplotlib>=3.10.7", ] [project.scripts] diff --git a/tool_call_qwen3_4b.png b/tool_call_qwen3_4b.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc00a6afdce3025fcbb463de882819b7a503c38 GIT binary patch literal 15378 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 literal 0 HcmV?d00001 diff --git a/uv.lock b/uv.lock index 6ea29413..f0b61c16 100644 --- a/uv.lock +++ b/uv.lock @@ -485,6 +485,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, +] + [[package]] name = "coverage" version = "7.11.0" @@ -615,6 +681,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/7c/15ad426257615f9be8caf7f97990cf3dcbb5b8dd7ed7e0db581a1c4759dd/cryptography-46.0.2-cp38-abi3-win_arm64.whl", hash = "sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1", size = 2918153, upload-time = "2025-10-01T00:28:51.003Z" }, ] +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + [[package]] name = "cyclopts" version = "3.24.0" @@ -849,6 +924,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, ] +[[package]] +name = "fonttools" +version = "4.60.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" }, + { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb", size = 2825777, upload-time = "2025-09-29T21:12:01.22Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8a/de9cc0540f542963ba5e8f3a1f6ad48fa211badc3177783b9d5cadf79b5d/fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4", size = 2348080, upload-time = "2025-09-29T21:12:03.785Z" }, + { url = "https://files.pythonhosted.org/packages/2d/8b/371ab3cec97ee3fe1126b3406b7abd60c8fec8975fd79a3c75cdea0c3d83/fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c", size = 4903082, upload-time = "2025-09-29T21:12:06.382Z" }, + { url = "https://files.pythonhosted.org/packages/04/05/06b1455e4bc653fcb2117ac3ef5fa3a8a14919b93c60742d04440605d058/fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77", size = 4960125, upload-time = "2025-09-29T21:12:09.314Z" }, + { url = "https://files.pythonhosted.org/packages/8e/37/f3b840fcb2666f6cb97038793606bdd83488dca2d0b0fc542ccc20afa668/fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199", size = 4901454, upload-time = "2025-09-29T21:12:11.931Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9e/eb76f77e82f8d4a46420aadff12cec6237751b0fb9ef1de373186dcffb5f/fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c", size = 5044495, upload-time = "2025-09-29T21:12:15.241Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b3/cede8f8235d42ff7ae891bae8d619d02c8ac9fd0cfc450c5927a6200c70d/fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272", size = 2217028, upload-time = "2025-09-29T21:12:17.96Z" }, + { url = "https://files.pythonhosted.org/packages/75/4d/b022c1577807ce8b31ffe055306ec13a866f2337ecee96e75b24b9b753ea/fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac", size = 2266200, upload-time = "2025-09-29T21:12:20.14Z" }, + { url = "https://files.pythonhosted.org/packages/9a/83/752ca11c1aa9a899b793a130f2e466b79ea0cf7279c8d79c178fc954a07b/fonttools-4.60.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:a884aef09d45ba1206712c7dbda5829562d3fea7726935d3289d343232ecb0d3", size = 2822830, upload-time = "2025-09-29T21:12:24.406Z" }, + { url = "https://files.pythonhosted.org/packages/57/17/bbeab391100331950a96ce55cfbbff27d781c1b85ebafb4167eae50d9fe3/fonttools-4.60.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8a44788d9d91df72d1a5eac49b31aeb887a5f4aab761b4cffc4196c74907ea85", size = 2345524, upload-time = "2025-09-29T21:12:26.819Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2e/d4831caa96d85a84dd0da1d9f90d81cec081f551e0ea216df684092c6c97/fonttools-4.60.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e852d9dda9f93ad3651ae1e3bb770eac544ec93c3807888798eccddf84596537", size = 4843490, upload-time = "2025-09-29T21:12:29.123Z" }, + { url = "https://files.pythonhosted.org/packages/49/13/5e2ea7c7a101b6fc3941be65307ef8df92cbbfa6ec4804032baf1893b434/fonttools-4.60.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:154cb6ee417e417bf5f7c42fe25858c9140c26f647c7347c06f0cc2d47eff003", size = 4944184, upload-time = "2025-09-29T21:12:31.414Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2b/cf9603551c525b73fc47c52ee0b82a891579a93d9651ed694e4e2cd08bb8/fonttools-4.60.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5664fd1a9ea7f244487ac8f10340c4e37664675e8667d6fee420766e0fb3cf08", size = 4890218, upload-time = "2025-09-29T21:12:33.936Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2f/933d2352422e25f2376aae74f79eaa882a50fb3bfef3c0d4f50501267101/fonttools-4.60.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:583b7f8e3c49486e4d489ad1deacfb8d5be54a8ef34d6df824f6a171f8511d99", size = 4999324, upload-time = "2025-09-29T21:12:36.637Z" }, + { url = "https://files.pythonhosted.org/packages/38/99/234594c0391221f66216bc2c886923513b3399a148defaccf81dc3be6560/fonttools-4.60.1-cp314-cp314-win32.whl", hash = "sha256:66929e2ea2810c6533a5184f938502cfdaea4bc3efb7130d8cc02e1c1b4108d6", size = 2220861, upload-time = "2025-09-29T21:12:39.108Z" }, + { url = "https://files.pythonhosted.org/packages/3e/1d/edb5b23726dde50fc4068e1493e4fc7658eeefcaf75d4c5ffce067d07ae5/fonttools-4.60.1-cp314-cp314-win_amd64.whl", hash = "sha256:f3d5be054c461d6a2268831f04091dc82753176f6ea06dc6047a5e168265a987", size = 2270934, upload-time = "2025-09-29T21:12:41.339Z" }, + { url = "https://files.pythonhosted.org/packages/fb/da/1392aaa2170adc7071fe7f9cfd181a5684a7afcde605aebddf1fb4d76df5/fonttools-4.60.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:b6379e7546ba4ae4b18f8ae2b9bc5960936007a1c0e30b342f662577e8bc3299", size = 2894340, upload-time = "2025-09-29T21:12:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a7/3b9f16e010d536ce567058b931a20b590d8f3177b2eda09edd92e392375d/fonttools-4.60.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9d0ced62b59e0430b3690dbc5373df1c2aa7585e9a8ce38eff87f0fd993c5b01", size = 2375073, upload-time = "2025-09-29T21:12:46.437Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/e9bcf51980f98e59bb5bb7c382a63c6f6cac0eec5f67de6d8f2322382065/fonttools-4.60.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:875cb7764708b3132637f6c5fb385b16eeba0f7ac9fa45a69d35e09b47045801", size = 4849758, upload-time = "2025-09-29T21:12:48.694Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/1d2cf7d1cba82264b2f8385db3f5960e3d8ce756b4dc65b700d2c496f7e9/fonttools-4.60.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a184b2ea57b13680ab6d5fbde99ccef152c95c06746cb7718c583abd8f945ccc", size = 5085598, upload-time = "2025-09-29T21:12:51.081Z" }, + { url = "https://files.pythonhosted.org/packages/5d/4d/279e28ba87fb20e0c69baf72b60bbf1c4d873af1476806a7b5f2b7fac1ff/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:026290e4ec76583881763fac284aca67365e0be9f13a7fb137257096114cb3bc", size = 4957603, upload-time = "2025-09-29T21:12:53.423Z" }, + { url = "https://files.pythonhosted.org/packages/78/d4/ff19976305e0c05aa3340c805475abb00224c954d3c65e82c0a69633d55d/fonttools-4.60.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f0e8817c7d1a0c2eedebf57ef9a9896f3ea23324769a9a2061a80fe8852705ed", size = 4974184, upload-time = "2025-09-29T21:12:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/63/22/8553ff6166f5cd21cfaa115aaacaa0dc73b91c079a8cfd54a482cbc0f4f5/fonttools-4.60.1-cp314-cp314t-win32.whl", hash = "sha256:1410155d0e764a4615774e5c2c6fc516259fe3eca5882f034eb9bfdbee056259", size = 2282241, upload-time = "2025-09-29T21:12:58.179Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/fa7b4d148e11d5a72761a22e595344133e83a9507a4c231df972e657579b/fonttools-4.60.1-cp314-cp314t-win_amd64.whl", hash = "sha256:022beaea4b73a70295b688f817ddc24ed3e3418b5036ffcd5658141184ef0d0c", size = 2345760, upload-time = "2025-09-29T21:13:00.375Z" }, + { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" }, +] + [[package]] name = "frozenlist" version = "1.7.0" @@ -1470,6 +1586,78 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] +[[package]] +name = "kiwisolver" +version = "1.4.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, + { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, + { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, + { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, + { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, + { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, + { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, + { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, + { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, + { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, + { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, + { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, + { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, + { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, + { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, + { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, + { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, + { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, + { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, + { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, + { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, + { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, + { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, + { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, + { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, + { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, + { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, + { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, + { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, + { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, + { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, + { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, + { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, + { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, +] + [[package]] name = "lazy-object-proxy" version = "1.12.0" @@ -1623,6 +1811,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739, upload-time = "2024-10-18T15:21:42.784Z" }, ] +[[package]] +name = "matplotlib" +version = "3.10.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/e2/d2d5295be2f44c678ebaf3544ba32d20c1f9ef08c49fe47f496180e1db15/matplotlib-3.10.7.tar.gz", hash = "sha256:a06ba7e2a2ef9131c79c49e63dad355d2d878413a0376c1727c8b9335ff731c7", size = 34804865, upload-time = "2025-10-09T00:28:00.669Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b3/09eb0f7796932826ec20c25b517d568627754f6c6462fca19e12c02f2e12/matplotlib-3.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a0edb7209e21840e8361e91ea84ea676658aa93edd5f8762793dec77a4a6748", size = 8272389, upload-time = "2025-10-09T00:26:42.474Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/1ae80ddafb8652fd8046cb5c8460ecc8d4afccb89e2c6d6bec61e04e1eaf/matplotlib-3.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c380371d3c23e0eadf8ebff114445b9f970aff2010198d498d4ab4c3b41eea4f", size = 8128247, upload-time = "2025-10-09T00:26:44.77Z" }, + { url = "https://files.pythonhosted.org/packages/7d/18/95ae2e242d4a5c98bd6e90e36e128d71cf1c7e39b0874feaed3ef782e789/matplotlib-3.10.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d5f256d49fea31f40f166a5e3131235a5d2f4b7f44520b1cf0baf1ce568ccff0", size = 8696996, upload-time = "2025-10-09T00:26:46.792Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3d/5b559efc800bd05cb2033aa85f7e13af51958136a48327f7c261801ff90a/matplotlib-3.10.7-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11ae579ac83cdf3fb72573bb89f70e0534de05266728740d478f0f818983c695", size = 9530153, upload-time = "2025-10-09T00:26:49.07Z" }, + { url = "https://files.pythonhosted.org/packages/88/57/eab4a719fd110312d3c220595d63a3c85ec2a39723f0f4e7fa7e6e3f74ba/matplotlib-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4c14b6acd16cddc3569a2d515cfdd81c7a68ac5639b76548cfc1a9e48b20eb65", size = 9593093, upload-time = "2025-10-09T00:26:51.067Z" }, + { url = "https://files.pythonhosted.org/packages/31/3c/80816f027b3a4a28cd2a0a6ef7f89a2db22310e945cd886ec25bfb399221/matplotlib-3.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:0d8c32b7ea6fb80b1aeff5a2ceb3fb9778e2759e899d9beff75584714afcc5ee", size = 8122771, upload-time = "2025-10-09T00:26:53.296Z" }, + { url = "https://files.pythonhosted.org/packages/de/77/ef1fc78bfe99999b2675435cc52120887191c566b25017d78beaabef7f2d/matplotlib-3.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:5f3f6d315dcc176ba7ca6e74c7768fb7e4cf566c49cb143f6bc257b62e634ed8", size = 7992812, upload-time = "2025-10-09T00:26:54.882Z" }, + { url = "https://files.pythonhosted.org/packages/02/9c/207547916a02c78f6bdd83448d9b21afbc42f6379ed887ecf610984f3b4e/matplotlib-3.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1d9d3713a237970569156cfb4de7533b7c4eacdd61789726f444f96a0d28f57f", size = 8273212, upload-time = "2025-10-09T00:26:56.752Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/b3d3338d467d3fc937f0bb7f256711395cae6f78e22cef0656159950adf0/matplotlib-3.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37a1fea41153dd6ee061d21ab69c9cf2cf543160b1b85d89cd3d2e2a7902ca4c", size = 8128713, upload-time = "2025-10-09T00:26:59.001Z" }, + { url = "https://files.pythonhosted.org/packages/22/ff/6425bf5c20d79aa5b959d1ce9e65f599632345391381c9a104133fe0b171/matplotlib-3.10.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b3c4ea4948d93c9c29dc01c0c23eef66f2101bf75158c291b88de6525c55c3d1", size = 8698527, upload-time = "2025-10-09T00:27:00.69Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7f/ccdca06f4c2e6c7989270ed7829b8679466682f4cfc0f8c9986241c023b6/matplotlib-3.10.7-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22df30ffaa89f6643206cf13877191c63a50e8f800b038bc39bee9d2d4957632", size = 9529690, upload-time = "2025-10-09T00:27:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/b8/95/b80fc2c1f269f21ff3d193ca697358e24408c33ce2b106a7438a45407b63/matplotlib-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b69676845a0a66f9da30e87f48be36734d6748024b525ec4710be40194282c84", size = 9593732, upload-time = "2025-10-09T00:27:04.653Z" }, + { url = "https://files.pythonhosted.org/packages/e1/b6/23064a96308b9aeceeffa65e96bcde459a2ea4934d311dee20afde7407a0/matplotlib-3.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:744991e0cc863dd669c8dc9136ca4e6e0082be2070b9d793cbd64bec872a6815", size = 8122727, upload-time = "2025-10-09T00:27:06.814Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a6/2faaf48133b82cf3607759027f82b5c702aa99cdfcefb7f93d6ccf26a424/matplotlib-3.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:fba2974df0bf8ce3c995fa84b79cde38326e0f7b5409e7a3a481c1141340bcf7", size = 7992958, upload-time = "2025-10-09T00:27:08.567Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f0/b018fed0b599bd48d84c08794cb242227fe3341952da102ee9d9682db574/matplotlib-3.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:932c55d1fa7af4423422cb6a492a31cbcbdbe68fd1a9a3f545aa5e7a143b5355", size = 8316849, upload-time = "2025-10-09T00:27:10.254Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b7/bb4f23856197659f275e11a2a164e36e65e9b48ea3e93c4ec25b4f163198/matplotlib-3.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e38c2d581d62ee729a6e144c47a71b3f42fb4187508dbbf4fe71d5612c3433b", size = 8178225, upload-time = "2025-10-09T00:27:12.241Z" }, + { url = "https://files.pythonhosted.org/packages/62/56/0600609893ff277e6f3ab3c0cef4eafa6e61006c058e84286c467223d4d5/matplotlib-3.10.7-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:786656bb13c237bbcebcd402f65f44dd61ead60ee3deb045af429d889c8dbc67", size = 8711708, upload-time = "2025-10-09T00:27:13.879Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1a/6bfecb0cafe94d6658f2f1af22c43b76cf7a1c2f0dc34ef84cbb6809617e/matplotlib-3.10.7-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09d7945a70ea43bf9248f4b6582734c2fe726723204a76eca233f24cffc7ef67", size = 9541409, upload-time = "2025-10-09T00:27:15.684Z" }, + { url = "https://files.pythonhosted.org/packages/08/50/95122a407d7f2e446fd865e2388a232a23f2b81934960ea802f3171518e4/matplotlib-3.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d0b181e9fa8daf1d9f2d4c547527b167cb8838fc587deabca7b5c01f97199e84", size = 9594054, upload-time = "2025-10-09T00:27:17.547Z" }, + { url = "https://files.pythonhosted.org/packages/13/76/75b194a43b81583478a81e78a07da8d9ca6ddf50dd0a2ccabf258059481d/matplotlib-3.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:31963603041634ce1a96053047b40961f7a29eb8f9a62e80cc2c0427aa1d22a2", size = 8200100, upload-time = "2025-10-09T00:27:20.039Z" }, + { url = "https://files.pythonhosted.org/packages/f5/9e/6aefebdc9f8235c12bdeeda44cc0383d89c1e41da2c400caf3ee2073a3ce/matplotlib-3.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:aebed7b50aa6ac698c90f60f854b47e48cd2252b30510e7a1feddaf5a3f72cbf", size = 8042131, upload-time = "2025-10-09T00:27:21.608Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4b/e5bc2c321b6a7e3a75638d937d19ea267c34bd5a90e12bee76c4d7c7a0d9/matplotlib-3.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d883460c43e8c6b173fef244a2341f7f7c0e9725c7fe68306e8e44ed9c8fb100", size = 8273787, upload-time = "2025-10-09T00:27:23.27Z" }, + { url = "https://files.pythonhosted.org/packages/86/ad/6efae459c56c2fbc404da154e13e3a6039129f3c942b0152624f1c621f05/matplotlib-3.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07124afcf7a6504eafcb8ce94091c5898bbdd351519a1beb5c45f7a38c67e77f", size = 8131348, upload-time = "2025-10-09T00:27:24.926Z" }, + { url = "https://files.pythonhosted.org/packages/a6/5a/a4284d2958dee4116359cc05d7e19c057e64ece1b4ac986ab0f2f4d52d5a/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c17398b709a6cce3d9fdb1595c33e356d91c098cd9486cb2cc21ea2ea418e715", size = 9533949, upload-time = "2025-10-09T00:27:26.704Z" }, + { url = "https://files.pythonhosted.org/packages/de/ff/f3781b5057fa3786623ad8976fc9f7b0d02b2f28534751fd5a44240de4cf/matplotlib-3.10.7-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7146d64f561498764561e9cd0ed64fcf582e570fc519e6f521e2d0cfd43365e1", size = 9804247, upload-time = "2025-10-09T00:27:28.514Z" }, + { url = "https://files.pythonhosted.org/packages/47/5a/993a59facb8444efb0e197bf55f545ee449902dcee86a4dfc580c3b61314/matplotlib-3.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:90ad854c0a435da3104c01e2c6f0028d7e719b690998a2333d7218db80950722", size = 9595497, upload-time = "2025-10-09T00:27:30.418Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a5/77c95aaa9bb32c345cbb49626ad8eb15550cba2e6d4c88081a6c2ac7b08d/matplotlib-3.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:4645fc5d9d20ffa3a39361fcdbcec731382763b623b72627806bf251b6388866", size = 8252732, upload-time = "2025-10-09T00:27:32.332Z" }, + { url = "https://files.pythonhosted.org/packages/74/04/45d269b4268d222390d7817dae77b159651909669a34ee9fdee336db5883/matplotlib-3.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:9257be2f2a03415f9105c486d304a321168e61ad450f6153d77c69504ad764bb", size = 8124240, upload-time = "2025-10-09T00:27:33.94Z" }, + { url = "https://files.pythonhosted.org/packages/4b/c7/ca01c607bb827158b439208c153d6f14ddb9fb640768f06f7ca3488ae67b/matplotlib-3.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1e4bbad66c177a8fdfa53972e5ef8be72a5f27e6a607cec0d8579abd0f3102b1", size = 8316938, upload-time = "2025-10-09T00:27:35.534Z" }, + { url = "https://files.pythonhosted.org/packages/84/d2/5539e66e9f56d2fdec94bb8436f5e449683b4e199bcc897c44fbe3c99e28/matplotlib-3.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d8eb7194b084b12feb19142262165832fc6ee879b945491d1c3d4660748020c4", size = 8178245, upload-time = "2025-10-09T00:27:37.334Z" }, + { url = "https://files.pythonhosted.org/packages/77/b5/e6ca22901fd3e4fe433a82e583436dd872f6c966fca7e63cf806b40356f8/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d41379b05528091f00e1728004f9a8d7191260f3862178b88e8fd770206318", size = 9541411, upload-time = "2025-10-09T00:27:39.387Z" }, + { url = "https://files.pythonhosted.org/packages/9e/99/a4524db57cad8fee54b7237239a8f8360bfcfa3170d37c9e71c090c0f409/matplotlib-3.10.7-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a74f79fafb2e177f240579bc83f0b60f82cc47d2f1d260f422a0627207008ca", size = 9803664, upload-time = "2025-10-09T00:27:41.492Z" }, + { url = "https://files.pythonhosted.org/packages/e6/a5/85e2edf76ea0ad4288d174926d9454ea85f3ce5390cc4e6fab196cbf250b/matplotlib-3.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:702590829c30aada1e8cef0568ddbffa77ca747b4d6e36c6d173f66e301f89cc", size = 9594066, upload-time = "2025-10-09T00:27:43.694Z" }, + { url = "https://files.pythonhosted.org/packages/39/69/9684368a314f6d83fe5c5ad2a4121a3a8e03723d2e5c8ea17b66c1bad0e7/matplotlib-3.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:f79d5de970fc90cd5591f60053aecfce1fcd736e0303d9f0bf86be649fa68fb8", size = 8342832, upload-time = "2025-10-09T00:27:45.543Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/e22e08da14bc1a0894184640d47819d2338b792732e20d292bf86e5ab785/matplotlib-3.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:cb783436e47fcf82064baca52ce748af71725d0352e1d31564cbe9c95df92b9c", size = 8172585, upload-time = "2025-10-09T00:27:47.185Z" }, +] + [[package]] name = "mcp" version = "1.16.0" @@ -1977,6 +2219,7 @@ dependencies = [ { name = "datasets" }, { name = "huggingface-hub" }, { name = "jinja2" }, + { name = "matplotlib" }, { name = "modal" }, { name = "openhands-agent-server" }, { name = "openhands-sdk" }, @@ -2007,6 +2250,7 @@ requires-dist = [ { name = "datasets" }, { name = "huggingface-hub" }, { name = "jinja2" }, + { name = "matplotlib", specifier = ">=3.10.7" }, { name = "modal", specifier = ">=1.1.4" }, { name = "openhands-agent-server", editable = "vendor/software-agent-sdk/openhands-agent-server" }, { name = "openhands-sdk", editable = "vendor/software-agent-sdk/openhands-sdk" }, From 479c0818c092defe6d6a875c465ab08849c5a8d6 Mon Sep 17 00:00:00 2001 From: adityasoni9998 Date: Thu, 4 Dec 2025 10:40:21 -0500 Subject: [PATCH 12/20] module-level rewards --- benchmarks/agentic_code_search/eval_infer2.py | 30 + .../agentic_code_search/module_rewards.py | 166 ++++++ .../prompts/file_module.j2 | 30 + benchmarks/agentic_code_search/run_infer2.py | 525 ++++++++++++++++++ benchmarks/agentic_code_search/run_infer2.sh | 8 + gt_location.jsonl | 300 ++++++++++ 6 files changed, 1059 insertions(+) create mode 100644 benchmarks/agentic_code_search/eval_infer2.py create mode 100644 benchmarks/agentic_code_search/module_rewards.py create mode 100644 benchmarks/agentic_code_search/prompts/file_module.j2 create mode 100644 benchmarks/agentic_code_search/run_infer2.py create mode 100644 benchmarks/agentic_code_search/run_infer2.sh create mode 100644 gt_location.jsonl diff --git a/benchmarks/agentic_code_search/eval_infer2.py b/benchmarks/agentic_code_search/eval_infer2.py new file mode 100644 index 00000000..e2f3bf92 --- /dev/null +++ b/benchmarks/agentic_code_search/eval_infer2.py @@ -0,0 +1,30 @@ +import json +from argparse import ArgumentParser + + +def main(args): + results_file = args.results_file + f1_file = 0 + f1_function = 0 + f1_module = 0 + cnt = 0 + with open(results_file, "r") as f: + for line in f: + result = json.loads(line) + reward_dict = result["test_result"]["reward"] + cnt += 1 + if reward_dict is not None: + f1_file += reward_dict.get("file_reward", 0) + f1_module += reward_dict.get("module_reward", 0) + f1_function += reward_dict.get("entity_reward", 0) + + print(f"Average File F1 score: {f1_file / cnt:.4f} over {cnt} samples") + print(f"Average Module F1 score: {f1_module / cnt:.4f} over {cnt} samples") + print(f"Average Function F1 score: {f1_function / cnt:.4f} over {cnt} samples") + + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("--results_file", type=str, required=True) + args = parser.parse_args() + main(args) diff --git a/benchmarks/agentic_code_search/module_rewards.py b/benchmarks/agentic_code_search/module_rewards.py new file mode 100644 index 00000000..22fa5341 --- /dev/null +++ b/benchmarks/agentic_code_search/module_rewards.py @@ -0,0 +1,166 @@ +import logging +from typing import Dict, List, Tuple + + +def parse_simple_output(raw_output: str) -> List[Dict[str, str]]: + """ + Parse simplified agent output containing filename, optional class, and function. + + Args: + raw_output: Raw text output from the agent + + Returns: + List of dictionaries with keys: 'file', 'class' (optional), 'function' + + Example input format: + ``` + path/to/file1.py + class: MyClass + function: my_method + + path/to/file2.py + function: standalone_function + ``` + + Example output: + [ + {'file': 'path/to/file1.py', 'class': 'MyClass', 'function': 'my_method'}, + {'file': 'path/to/file2.py', 'class': None, 'function': 'standalone_function'} + ] + """ + # Remove triple backticks and whitespace + raw_output = raw_output.strip("` \n") + + locations = [] + current_file = None + current_class = None + + lines = raw_output.strip().split("\n") + + for line in lines: + line = line.strip() + + if not line: + # Empty line resets the current class context + current_class = None + continue + + # Check if this is a Python file path + if line.endswith(".py"): + current_file = line + current_class = None + continue + + # Parse class declaration + if line.startswith("class:"): + class_name = line[len("class:") :].strip() + current_class = class_name + continue + + # Parse function/method declaration + if line.startswith("function:") or line.startswith("method:"): + if not current_file: + logging.warning(f"Found function/method without a file: {line}") + continue + + func_text = line.split(":", 1)[1].strip() + func_name = func_text.split()[0].strip("() ") + + # Check if function includes class prefix (e.g., "MyClass.my_method") + if "." in func_name: + parts = func_name.split(".", 1) + class_name = parts[0] + method_name = parts[1] + + locations.append( + {"file": current_file, "class": class_name, "function": method_name} + ) + else: + # Standalone function or method within current class context + locations.append( + { + "file": current_file, + "class": current_class, + "function": func_name, + } + ) + + return locations + + +def convert_to_entity_format(locations: List[Dict[str, str]]) -> List[str]: + """ + Convert location dictionaries to entity identifier format. + + Args: + locations: List of dicts with 'file', 'class', 'function' keys + + Returns: + List of entity identifiers in format 'file.py:ClassName.function_name' + or 'file.py:function_name' for standalone functions + + Example: + Input: [{'file': 'test.py', 'class': 'MyClass', 'function': 'method'}] + Output: ['test.py:MyClass.method'] + """ + entities = [] + + for loc in locations: + file_path = loc["file"] + class_name = loc.get("class") + func_name = loc["function"] + + if class_name: + entity = f"{file_path}:{class_name}.{func_name}" + else: + entity = f"{file_path}:{func_name}" + if entity.endswith(".__init__"): + entity = entity[: (len(entity) - len(".__init__"))] + entities.append(entity) + entities = list(set(entities)) # Remove duplicates + return entities + + +def get_simple_results_from_raw_outputs( + raw_output: str, +) -> Tuple[List[str], List[str], List[str]]: + """ + Process raw output and extract files, modules, and entities. + + This is a simplified version of get_loc_results_from_raw_outputs() that + doesn't require a dependency graph for validation. + + Args: + raw_output: Raw text output from the agent + + Returns: + Tuple of (all_found_files, all_found_modules, all_found_entities) + where each is a list of strs + """ + all_found_files = [] + all_found_modules = [] + all_found_entities = [] + + locations = parse_simple_output(raw_output) + files = list(set([loc["file"] for loc in locations])) + # Convert to entity format + entities = convert_to_entity_format(locations) + + # Extract modules (file:class or file if no class) + modules = [] + for entity in entities: + # Extract module (class or just file if standalone function) + if "." in entity.split(":")[-1]: + # Has a class - extract it: "file.py:Class.method" → "file.py:Class" + module = entity.rsplit(".", 1)[0] + else: + # No class - use full entity: "file.py:function" → "file.py:function" + module = entity + if module not in modules: + modules.append(module) + + all_found_files = files + all_found_modules = list(set(modules)) + all_found_entities = entities + + return all_found_files, all_found_modules, all_found_entities diff --git a/benchmarks/agentic_code_search/prompts/file_module.j2 b/benchmarks/agentic_code_search/prompts/file_module.j2 new file mode 100644 index 00000000..1c803611 --- /dev/null +++ b/benchmarks/agentic_code_search/prompts/file_module.j2 @@ -0,0 +1,30 @@ +I have access to a python code repository in the directory {{ working_dir }} . Consider the following issue description: + + +{{ problem_statement }} + + +Act as a code search agent and localize the specific files, classes or functions of code that need modification to resolve the issue in . + +NOTE: You do not need to solve the issue, all you need to do is localize relevant code from the repository. Your output will be used to guide another agent to solve the issue. + +Your final output should list the locations requiring modification, wrapped with triple backticks ``` +Each location should include the file path, class name (if applicable), and function name. Here is an example Output: +``` +full_path1/file1.py +class: MyClass1 +function: my_function1 + +full_path2/file2.py +function: MyClass2.my_function2 + +full_path3/file3.py +function: my_function3 +``` + +IMPORTANT: Your output MUST follow the below rules: +1. The final output must be returned in the message parameter of the Finish tool wrapped within ```, and there should be NO text outside these triple backticks (```). +2. The locations of the file path must be RELATIVE to the {{ working_dir }} directory WITHOUT any leading "./" in the output. +3. For each localized code output, you MUST always include the file path and the function name. If the function is within a class you MUST also include the class name. +4. Only include those locations in your output that need modification to resolve the issue in . Do NOT include any locations that do not need modification. + diff --git a/benchmarks/agentic_code_search/run_infer2.py b/benchmarks/agentic_code_search/run_infer2.py new file mode 100644 index 00000000..7f7eed01 --- /dev/null +++ b/benchmarks/agentic_code_search/run_infer2.py @@ -0,0 +1,525 @@ +import json +import os +from argparse import ArgumentParser +from pathlib import Path +from typing import List + +import pandas as pd +from jinja2 import Environment, FileSystemLoader + +from benchmarks.utils.dataset import prepare_dataset + +# from benchmarks.utils.args_parser import get_parser +from benchmarks.utils.evaluation import Evaluation +from benchmarks.utils.evaluation_utils import ( + construct_eval_output_dir, + get_default_on_result_writer, +) +from benchmarks.utils.models import ( + EvalInstance, + EvalMetadata, + EvalOutput, +) +from openhands.sdk import LLM, Agent, Conversation, get_logger +from openhands.sdk.conversation import get_agent_final_response +from openhands.sdk.workspace import LocalWorkspace, RemoteWorkspace +from openhands.tools.preset.default import get_default_tools +from openhands.workspace import DockerWorkspace + + +logger = get_logger(__name__) + + +def get_parser(): + """Create and return argument parser. + + Returns: + ArgumentParser instance + """ + prompt_dir = (Path(__file__).parent / "prompts").resolve() + default_prompt_path = prompt_dir / "file_module.j2" + assert default_prompt_path.exists(), ( + f"Default prompt path file {default_prompt_path} not found" + ) + parser = ArgumentParser(description="Run inference on code localization dataset") + parser.add_argument( + "--dataset_file", + type=str, + required=True, + help="Path of the prepared dataset JSONL file.", + ) + parser.add_argument("--split", type=str, default="test", help="Dataset split") + parser.add_argument( + "--llm-config-path", + type=str, + required=True, + help="Path to JSON LLM configuration", + ) + parser.add_argument( + "--max-iterations", + type=int, + default=25, + help="Maximum steps allowed for the agent", + ) + parser.add_argument( + "--num-workers", type=int, default=1, help="Number of evaluation workers" + ) + # parser.add_argument("--note", type=str, default="initial", help="Evaluation note") + parser.add_argument( + "--output-dir", + type=str, + default="./evaluation_outputs", + help="Evaluation output directory", + ) + parser.add_argument( + "--n-limit", + type=int, + default=-1, + help="Limit number of instances to evaluate", + ) + parser.add_argument( + "--prompt-path", + type=str, + default=str(default_prompt_path), + help="Path to prompt template file", + ) + + parser.add_argument( + "--runtime", + type=str, + default="local", + choices=["local", "docker"], + help="Runtime environment for the agent (local or docker)", + ) + parser.add_argument( + "--select", + type=str, + default="", + help="Path to text file containing instance IDs to select (one per line)", + ) + parser.add_argument( + "--workspace_base_dir", + type=str, + default="/tmp/workspace/", + help="Base directory for local workspaces (ignored for remote workspaces)", + ) + return parser + + +def get_instruction(instance: EvalInstance, metadata: EvalMetadata) -> str: + working_dir = instance.data["repo_dir"] + problem_statement = instance.data["problem_statement"] + if metadata.prompt_path is not None: + prompts_dir = os.path.dirname(metadata.prompt_path) + template_name = os.path.basename(metadata.prompt_path) + env = Environment(loader=FileSystemLoader(prompts_dir)) + template = env.get_template(template_name) + context = {"problem_statement": problem_statement, "working_dir": working_dir} + instruction = template.render(context) + return instruction + else: + raise ValueError("metadata.prompt_path is None") + + +def f1_reward_function(predicted_set, true_set): + if len(true_set) == 0: + return 0 + tp = len(predicted_set & true_set) + precision = tp / len(predicted_set) if predicted_set else 0.0 + recall = tp / len(true_set) if true_set else 0.0 + if not predicted_set and not true_set: + return 1.0 + return ( + 0.0 + if precision + recall == 0 + else 2 * precision * recall / (precision + recall) + ) + + +def parse_simple_output(raw_output: str, repo_dir: str) -> List: + # Remove triple backticks and whitespace + raw_output = raw_output.strip("` \n") + if not repo_dir.endswith("/"): + repo_dir += "/" + locations = [] + current_file = None + current_class = None + lines = raw_output.strip().split("\n") + + for line in lines: + line = line.strip() + + if not line: + current_file = None + current_class = None + continue + + # Check if this is a Python file path + if line.endswith(".py"): + current_file = line.strip() + if current_file.startswith("./"): + current_file = current_file[2:] # Remove leading ./ + elif current_file.startswith(repo_dir): + current_file = current_file[ + len(repo_dir) : + ] # make absolute path relative + continue + + # Parse class declaration + if line.startswith("class:"): + class_name = line[len("class:") :].strip() + class_name = class_name.split()[0] # Take first word only + current_class = class_name + continue + + # Parse function/method declaration + if line.startswith("function:") or line.startswith("method:"): + if not current_file: + logger.info(f"WARNING: Found function/method without a file: {line}") + continue + + func_text = line.split(":", 1)[1].strip() + func_name = func_text.split()[0].strip("() ") + + # Check if function includes class prefix (e.g., "MyClass.my_method") + if "." in func_name: + parts = func_name.split(".", 1) + class_name = parts[0].strip() + method_name = parts[1].strip() + + locations.append( + {"file": current_file, "class": class_name, "function": method_name} + ) + else: + # Standalone function or method within current class context + locations.append( + { + "file": current_file, + "class": current_class, + "function": func_name, + } + ) + current_file = None # Reset current file after function processed + current_class = None # Reset current class after function processed + + return locations + + +def convert_to_entity_format(locations: List) -> List[str]: + entities = [] + + for loc in locations: + file_path = loc["file"] + class_name = loc.get("class") + func_name = loc["function"] + + if class_name: + entity = f"{file_path}:{class_name}.{func_name}" + else: + entity = f"{file_path}:{func_name}" + + entities.append(entity) + + return list(set(entities)) + + +def process_raw_output(raw_output: str, repo_dir: str): + locations = parse_simple_output(raw_output, repo_dir) + + # Extract unique files + files = list(dict.fromkeys([loc["file"] for loc in locations])) + + # Extract modules (file:class or file if no class) + entities = convert_to_entity_format(locations) + modules = [] + for entity in entities: + # Extract module (class or just file if standalone function) + if "." in entity.split(":")[-1]: + # Has a class - extract it: "file.py:Class.method" → "file.py:Class" + module = entity.rsplit(".", 1)[0] + else: + # No class - use full entity: "file.py:function" → "file.py:function" + module = entity + if module not in modules: + modules.append(module) + + all_found_files = set(files) + all_found_modules = set(modules) + all_found_entities = set(entities) + return all_found_files, all_found_modules, all_found_entities + + +def reward_function(final_message: str, instance: dict) -> dict: + try: + gt_files = [] + gt_modules = [] + gt_entities = [] + + for change in instance.get("file_changes", []): + if "file" in change: + gt_files.append(change["file"]) + if "changes" in change: + for module in change["changes"].get("edited_modules", []): + gt_modules.append(module) + for entity in change["changes"].get("edited_entities", []): + gt_entities.append(entity) + gt_files = set(gt_files) + gt_modules = set(gt_modules) + gt_entities = set(gt_entities) + except Exception as e: + print(f"Error extracting ground truth: {e}") + return { + "file_reward": 0, + "module_reward": 0, + "entity_reward": 0, + "prediction": {}, + "ground_truth": {}, + } + + try: + predicted_files, predicted_modules, predicted_entities = process_raw_output( + final_message, instance["repo_dir"] + ) + except Exception as e: + print(f"Error processing raw output: {e}") + return { + "file_reward": 0, + "module_reward": 0, + "entity_reward": 0, + "prediction": {}, + "ground_truth": { + "files": list(gt_files), + "modules": list(gt_modules), + "entities": list(gt_entities), + }, + } + try: + file_f1_score = f1_reward_function(predicted_files, gt_files) + module_f1_score = f1_reward_function(predicted_modules, gt_modules) + entity_f1_score = f1_reward_function(predicted_entities, gt_entities) + return { + "file_reward": file_f1_score, + "module_reward": module_f1_score, + "entity_reward": entity_f1_score, + "prediction": { + "files": list(predicted_files), + "modules": list(predicted_modules), + "entities": list(predicted_entities), + }, + "ground_truth": { + "files": list(gt_files), + "modules": list(gt_modules), + "entities": list(gt_entities), + }, + } + except Exception as e: + print(f"Error computing F1 scores: {e}") + return { + "file_reward": 0, + "module_reward": 0, + "entity_reward": 0, + "prediction": { + "files": list(predicted_files), + "modules": list(predicted_modules), + "entities": list(predicted_entities), + }, + "ground_truth": { + "files": list(gt_files), + "modules": list(gt_modules), + "entities": list(gt_entities), + }, + } + + +class AgenticCodeSearchEvaluation(Evaluation): + def prepare_instances(self) -> List[EvalInstance]: + logger.info("Setting up agentic code search evaluation.") + + # Load dataset + instance_data = [] + with open(self.metadata.dataset, "r") as f: + for line in f: + data = json.loads(line.strip()) + instance_data.append(data) + # convert list to pandas dataframe + dataset = pd.DataFrame(instance_data) + dataset = prepare_dataset( + dataset, self.metadata.eval_limit, self.metadata.selected_instances_file + ) + + instances: List[EvalInstance] = [] + for _, row in dataset.iterrows(): + inst_id = str(row["instance_id"]) + instances.append(EvalInstance(id=inst_id, data=row.to_dict())) + + logger.info("Total instances to process: %d", len(instances)) + return instances + + def prepare_workspace(self, instance: EvalInstance): + runtime_type = ( + self.metadata.details.get("runtime", "local") + if isinstance(self.metadata.details, dict) + else "local" + ) + repo_name = instance.data["repo"] + if runtime_type == "local": + assert isinstance(self.metadata.details, dict) + working_dir = Path(self.metadata.details["workspace_base_dir"]).resolve() + # instance_dir_name = f"{repo_name.replace('/', '_')}_{instance_id}" + # working_dir = output_dir / instance_dir_name + workspace = LocalWorkspace(working_dir=str(working_dir)) + repo_dir = working_dir / instance.id + repo_dir = str(repo_dir) + elif runtime_type == "docker": + # TODO: directly use prebuilt agent-server image? + workspace = DockerWorkspace( + # base_image="nikolaik/python-nodejs:python3.12-nodejs22", + working_dir="/workspace", + server_image="ghcr.io/openhands/agent-server:latest-python", + ) + repo_dir = f"/workspace/{repo_name.split('/')[-1]}/" + else: + raise NotImplementedError(f"Unsupported runtime type: {runtime_type}") + + base_commit_id = instance.data["base_commit"] + instance.data["repo_dir"] = ( + repo_dir # pass repo_dir to instance data for later use + ) + + # run environment setup commands for cloning repo + # remove working dir if it already exists + rm_dir = workspace.execute_command(f"rm -rf {repo_dir}") + assert rm_dir.exit_code == 0, ( + f"Failed to remove existing working dir: {rm_dir.stderr}" + ) + + # make new empty directory + mk_dir = workspace.execute_command(f"mkdir -p {repo_dir}") + assert mk_dir.exit_code == 0, ( + f"Failed to create repo directory: {mk_dir.stderr}" + ) + + # clone repo inside repo_dir + repo_url = f"https://github.com/{repo_name}.git" + clone_repo = workspace.execute_command( + f"git clone {repo_url} {repo_dir}", timeout=10 * 60 + ) + assert clone_repo.exit_code == 0, f"Failed to clone repo: {clone_repo.stderr}" + + # checkout to base commit + checkout_commit = workspace.execute_command( + f"git -C {repo_dir} checkout {base_commit_id}", timeout=5 * 60 + ) + assert checkout_commit.exit_code == 0, ( + f"Failed to checkout to commit {base_commit_id}: {checkout_commit.stderr}" + ) + + logger.info(f"Prepared workspace successfully for instance {instance.id}") + return workspace + + def evaluate_instance(self, instance, workspace): + """ + Steps: + 1. Prepare the prompt using Jinja2 template + 2. Create agent with the prompt + 3. Run the agent in a conversation until max iterations or task completion + 4. Collect and return the output + """ + instruction = get_instruction(instance, self.metadata) + # NOTE: the default condenser is LLM-based summarizer in get_default_agent, disabling it for now as done in SWE-Bench. This is why we make agent manually here. + tools = get_default_tools(enable_browser=False) + agent = Agent( + llm=self.metadata.llm, + tools=tools, + system_prompt_kwargs={"cli_mode": True}, + ) + + def _log_event(ev): # keep it simple + logger.debug("Event: %s", ev) + + assert isinstance(workspace, RemoteWorkspace) or isinstance( + workspace, LocalWorkspace + ) + conversation = Conversation( + agent=agent, + workspace=workspace, + callbacks=[_log_event], + max_iteration_per_run=self.metadata.max_iterations, + ) + conversation.send_message(instruction) + conversation.run() + history = list(map(lambda event: event.model_dump(), conversation.state.events)) + finish_message = get_agent_final_response(conversation.state.events) + if finish_message == "": + logger.info("No final response from agent.") + reward_dict = reward_function(finish_message, instance.data) + if ( + self.metadata.details is not None + and self.metadata.details["runtime"] == "local" + ): + # clean up workspace after use + workspace.execute_command(f"rm -rf {instance.data['repo_dir']}") + out = EvalOutput( + instance_id=instance.id, + test_result={ + "reward": reward_dict, + "raw_prediction": finish_message, + }, + instruction=instruction, + error=None, + history=history, + metrics=conversation.conversation_stats.get_combined_metrics(), + ) + return out + + +def main(): + parser = get_parser() + args = parser.parse_args() + + # load LLM configuration + llm_config_path = args.llm_config_path + if not os.path.isfile(llm_config_path): + raise ValueError(f"LLM config file {llm_config_path} does not exist") + with open(llm_config_path, "r") as f: + llm_config = f.read() + llm = LLM.model_validate_json(llm_config) + logger.info("Using LLM config: %s", llm.model_dump_json(indent=2)) + + structured_output_dir = construct_eval_output_dir( + base_dir=args.output_dir, + dataset_name=f"agentic_code_search_{args.dataset_file.split('/')[-1].split('.jsonl')[0]}", + model_name=llm.model, + max_iterations=args.max_iterations, + eval_note="", + ) + + metadata = EvalMetadata( + llm=llm, + dataset=args.dataset_file, + dataset_split=args.split, + max_iterations=args.max_iterations, + eval_output_dir=structured_output_dir, + details={ + "runtime": args.runtime, + "workspace_base_dir": args.workspace_base_dir, + }, + prompt_path=args.prompt_path, + eval_limit=args.n_limit, + env_setup_commands=[], + # max_attempts=args.max_attempts, + # critic_name=args.critic, + selected_instances_file=args.select if args.select else None, + # max_retries=args.max_retries, + ) + + evaluator = AgenticCodeSearchEvaluation( + metadata=metadata, num_workers=args.num_workers + ) + evaluator.run(on_result=get_default_on_result_writer(evaluator.output_path)) + + logger.info("Evaluation completed!") + + +if __name__ == "__main__": + main() diff --git a/benchmarks/agentic_code_search/run_infer2.sh b/benchmarks/agentic_code_search/run_infer2.sh new file mode 100644 index 00000000..b5115e62 --- /dev/null +++ b/benchmarks/agentic_code_search/run_infer2.sh @@ -0,0 +1,8 @@ +uv run python -m benchmarks.agentic_code_search.run_infer2 \ + --dataset_file gt_location.jsonl \ + --llm-config-path .llm_config/qwen3.json \ + --max-iterations 25 \ + --num-workers 8 \ + --output-dir ./docker_outputs_module_function_level \ + --n-limit 500 \ + --runtime docker \ No newline at end of file diff --git a/gt_location.jsonl b/gt_location.jsonl new file mode 100644 index 00000000..bd3a653d --- /dev/null +++ b/gt_location.jsonl @@ -0,0 +1,300 @@ +{"instance_id": "astropy__astropy-12907", "file_changes": [{"file": "astropy/modeling/separable.py", "changes": {"edited_entities": ["astropy/modeling/separable.py:_cstack"], "edited_modules": ["astropy/modeling/separable.py:_cstack"]}}], "repo": "astropy/astropy", "base_commit": "d16bfe05a744909de4b27f5875fe0d4ed41ce607", "problem_statement": "Modeling's `separability_matrix` does not compute separability correctly for nested CompoundModels\nConsider the following model:\r\n\r\n```python\r\nfrom astropy.modeling import models as m\r\nfrom astropy.modeling.separable import separability_matrix\r\n\r\ncm = m.Linear1D(10) & m.Linear1D(5)\r\n```\r\n\r\nIt's separability matrix as you might expect is a diagonal:\r\n\r\n```python\r\n>>> separability_matrix(cm)\r\narray([[ True, False],\r\n [False, True]])\r\n```\r\n\r\nIf I make the model more complex:\r\n```python\r\n>>> separability_matrix(m.Pix2Sky_TAN() & m.Linear1D(10) & m.Linear1D(5))\r\narray([[ True, True, False, False],\r\n [ True, True, False, False],\r\n [False, False, True, False],\r\n [False, False, False, True]])\r\n```\r\n\r\nThe output matrix is again, as expected, the outputs and inputs to the linear models are separable and independent of each other.\r\n\r\nIf however, I nest these compound models:\r\n```python\r\n>>> separability_matrix(m.Pix2Sky_TAN() & cm)\r\narray([[ True, True, False, False],\r\n [ True, True, False, False],\r\n [False, False, True, True],\r\n [False, False, True, True]])\r\n```\r\nSuddenly the inputs and outputs are no longer separable?\r\n\r\nThis feels like a bug to me, but I might be missing something?\n", "patch": "diff --git a/astropy/modeling/separable.py b/astropy/modeling/separable.py\n--- a/astropy/modeling/separable.py\n+++ b/astropy/modeling/separable.py\n@@ -242,7 +242,7 @@ def _cstack(left, right):\n cright = _coord_matrix(right, 'right', noutp)\n else:\n cright = np.zeros((noutp, right.shape[1]))\n- cright[-right.shape[0]:, -right.shape[1]:] = 1\n+ cright[-right.shape[0]:, -right.shape[1]:] = right\n \n return np.hstack([cleft, cright])\n \n"} +{"instance_id": "astropy__astropy-14182", "file_changes": [{"file": "astropy/io/ascii/rst.py", "changes": {"edited_entities": ["astropy/io/ascii/rst.py:RST.__init__", "astropy/io/ascii/rst.py:RST.write"], "edited_modules": ["astropy/io/ascii/rst.py:RST"], "added_entities": ["astropy/io/ascii/rst.py:RST.read"], "added_modules": ["astropy/io/ascii/rst.py:RST"]}}], "repo": "astropy/astropy", "base_commit": "a5917978be39d13cd90b517e1de4e7a539ffaa48", "problem_statement": "Please support header rows in RestructuredText output\n### Description\r\n\r\nIt would be great if the following would work:\r\n\r\n```Python\r\n>>> from astropy.table import QTable\r\n>>> import astropy.units as u\r\n>>> import sys\r\n>>> tbl = QTable({'wave': [350,950]*u.nm, 'response': [0.7, 1.2]*u.count})\r\n>>> tbl.write(sys.stdout, format=\"ascii.rst\")\r\n===== ========\r\n wave response\r\n===== ========\r\n350.0 0.7\r\n950.0 1.2\r\n===== ========\r\n>>> tbl.write(sys.stdout, format=\"ascii.fixed_width\", header_rows=[\"name\", \"unit\"])\r\n| wave | response |\r\n| nm | ct |\r\n| 350.0 | 0.7 |\r\n| 950.0 | 1.2 |\r\n>>> tbl.write(sys.stdout, format=\"ascii.rst\", header_rows=[\"name\", \"unit\"])\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/usr/lib/python3/dist-packages/astropy/table/connect.py\", line 129, in __call__\r\n self.registry.write(instance, *args, **kwargs)\r\n File \"/usr/lib/python3/dist-packages/astropy/io/registry/core.py\", line 369, in write\r\n return writer(data, *args, **kwargs)\r\n File \"/usr/lib/python3/dist-packages/astropy/io/ascii/connect.py\", line 26, in io_write\r\n return write(table, filename, **kwargs)\r\n File \"/usr/lib/python3/dist-packages/astropy/io/ascii/ui.py\", line 856, in write\r\n writer = get_writer(Writer=Writer, fast_writer=fast_writer, **kwargs)\r\n File \"/usr/lib/python3/dist-packages/astropy/io/ascii/ui.py\", line 800, in get_writer\r\n writer = core._get_writer(Writer, fast_writer, **kwargs)\r\n File \"/usr/lib/python3/dist-packages/astropy/io/ascii/core.py\", line 1719, in _get_writer\r\n writer = Writer(**writer_kwargs)\r\nTypeError: RST.__init__() got an unexpected keyword argument 'header_rows'\r\n```\r\n\r\n\r\n### Additional context\r\n\r\nRestructuredText output is a great way to fill autogenerated documentation with content, so having this flexible makes the life easier `:-)`\r\n\r\n\n", "patch": "diff --git a/astropy/io/ascii/rst.py b/astropy/io/ascii/rst.py\n--- a/astropy/io/ascii/rst.py\n+++ b/astropy/io/ascii/rst.py\n@@ -27,7 +27,6 @@ def get_fixedwidth_params(self, line):\n \n \n class SimpleRSTData(FixedWidthData):\n- start_line = 3\n end_line = -1\n splitter_class = FixedWidthTwoLineDataSplitter\n \n@@ -39,12 +38,29 @@ class RST(FixedWidth):\n \n Example::\n \n- ==== ===== ======\n- Col1 Col2 Col3\n- ==== ===== ======\n- 1 2.3 Hello\n- 2 4.5 Worlds\n- ==== ===== ======\n+ >>> from astropy.table import QTable\n+ >>> import astropy.units as u\n+ >>> import sys\n+ >>> tbl = QTable({\"wave\": [350, 950] * u.nm, \"response\": [0.7, 1.2] * u.count})\n+ >>> tbl.write(sys.stdout, format=\"ascii.rst\")\n+ ===== ========\n+ wave response\n+ ===== ========\n+ 350.0 0.7\n+ 950.0 1.2\n+ ===== ========\n+\n+ Like other fixed-width formats, when writing a table you can provide ``header_rows``\n+ to specify a list of table rows to output as the header. For example::\n+\n+ >>> tbl.write(sys.stdout, format=\"ascii.rst\", header_rows=['name', 'unit'])\n+ ===== ========\n+ wave response\n+ nm ct\n+ ===== ========\n+ 350.0 0.7\n+ 950.0 1.2\n+ ===== ========\n \n Currently there is no support for reading tables which utilize continuation lines,\n or for ones which define column spans through the use of an additional\n@@ -57,10 +73,15 @@ class RST(FixedWidth):\n data_class = SimpleRSTData\n header_class = SimpleRSTHeader\n \n- def __init__(self):\n- super().__init__(delimiter_pad=None, bookend=False)\n+ def __init__(self, header_rows=None):\n+ super().__init__(delimiter_pad=None, bookend=False, header_rows=header_rows)\n \n def write(self, lines):\n lines = super().write(lines)\n- lines = [lines[1]] + lines + [lines[1]]\n+ idx = len(self.header.header_rows)\n+ lines = [lines[idx]] + lines + [lines[idx]]\n return lines\n+\n+ def read(self, table):\n+ self.data.start_line = 2 + len(self.header.header_rows)\n+ return super().read(table)\n"} +{"instance_id": "astropy__astropy-14365", "file_changes": [{"file": "astropy/io/ascii/qdp.py", "changes": {"edited_entities": ["astropy/io/ascii/qdp.py:_line_type", "astropy/io/ascii/qdp.py:_get_tables_from_qdp_file"], "edited_modules": ["astropy/io/ascii/qdp.py:_line_type", "astropy/io/ascii/qdp.py:_get_tables_from_qdp_file"]}}], "repo": "astropy/astropy", "base_commit": "7269fa3e33e8d02485a647da91a5a2a60a06af61", "problem_statement": "ascii.qdp Table format assumes QDP commands are upper case\n### Description\n\nascii.qdp assumes that commands in a QDP file are upper case, for example, for errors they must be \"READ SERR 1 2\" whereas QDP itself is not case sensitive and case use \"read serr 1 2\". \r\n\r\nAs many QDP files are created by hand, the expectation that all commands be all-caps should be removed.\n\n### Expected behavior\n\nThe following qdp file should read into a `Table` with errors, rather than crashing.\r\n```\r\nread serr 1 2 \r\n1 0.5 1 0.5\r\n```\n\n### How to Reproduce\n\nCreate a QDP file:\r\n```\r\n> cat > test.qdp\r\nread serr 1 2 \r\n1 0.5 1 0.5\r\n\r\n\r\n > python\r\nPython 3.10.9 (main, Dec 7 2022, 02:03:23) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n>>> from astropy.table import Table\r\n>>> Table.read('test.qdp',format='ascii.qdp')\r\nWARNING: table_id not specified. Reading the first available table [astropy.io.ascii.qdp]\r\nTraceback (most recent call last):\r\n...\r\n raise ValueError(f'Unrecognized QDP line: {line}')\r\nValueError: Unrecognized QDP line: read serr 1 2\r\n```\r\n\r\nRunning \"qdp test.qdp\" works just fine.\r\n\n\n### Versions\n\nPython 3.10.9 (main, Dec 7 2022, 02:03:23) [Clang 13.0.0 (clang-1300.0.29.30)]\r\nastropy 5.1\r\nNumpy 1.24.1\r\npyerfa 2.0.0.1\r\nScipy 1.10.0\r\nMatplotlib 3.6.3\r\n\n", "patch": "diff --git a/astropy/io/ascii/qdp.py b/astropy/io/ascii/qdp.py\n--- a/astropy/io/ascii/qdp.py\n+++ b/astropy/io/ascii/qdp.py\n@@ -68,7 +68,7 @@ def _line_type(line, delimiter=None):\n _new_re = rf\"NO({sep}NO)+\"\n _data_re = rf\"({_decimal_re}|NO|[-+]?nan)({sep}({_decimal_re}|NO|[-+]?nan))*)\"\n _type_re = rf\"^\\s*((?P{_command_re})|(?P{_new_re})|(?P{_data_re})?\\s*(\\!(?P.*))?\\s*$\"\n- _line_type_re = re.compile(_type_re)\n+ _line_type_re = re.compile(_type_re, re.IGNORECASE)\n line = line.strip()\n if not line:\n return \"comment\"\n@@ -306,7 +306,7 @@ def _get_tables_from_qdp_file(qdp_file, input_colnames=None, delimiter=None):\n \n values = []\n for v in line.split(delimiter):\n- if v == \"NO\":\n+ if v.upper() == \"NO\":\n values.append(np.ma.masked)\n else:\n # Understand if number is int or float\n"} +{"instance_id": "astropy__astropy-14995", "file_changes": [{"file": "astropy/nddata/mixins/ndarithmetic.py", "changes": {"edited_entities": ["astropy/nddata/mixins/ndarithmetic.py:NDArithmeticMixin._arithmetic_mask"], "edited_modules": ["astropy/nddata/mixins/ndarithmetic.py:NDArithmeticMixin"]}}], "repo": "astropy/astropy", "base_commit": "b16c7d12ccbc7b2d20364b89fb44285bcbfede54", "problem_statement": "In v5.3, NDDataRef mask propagation fails when one of the operand does not have a mask\n### Description\n\nThis applies to v5.3. \r\n\r\nIt looks like when one of the operand does not have a mask, the mask propagation when doing arithmetic, in particular with `handle_mask=np.bitwise_or` fails. This is not a problem in v5.2.\r\n\r\nI don't know enough about how all that works, but it seems from the error that the operand without a mask is set as a mask of None's and then the bitwise_or tries to operate on an integer and a None and fails.\n\n### Expected behavior\n\nWhen one of the operand does not have mask, the mask that exists should just be copied over to the output. Or whatever was done in that situation in v5.2 where there's no problem.\n\n### How to Reproduce\n\nThis is with v5.3. With v5.2, there are no errors.\r\n\r\n```\r\n>>> import numpy as np\r\n>>> from astropy.nddata import NDDataRef\r\n\r\n>>> array = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]])\r\n>>> mask = np.array([[0, 1, 64], [8, 0, 1], [2, 1, 0]])\r\n\r\n>>> nref_nomask = NDDataRef(array)\r\n>>> nref_mask = NDDataRef(array, mask=mask)\r\n\r\n# multiply no mask by constant (no mask * no mask)\r\n>>> nref_nomask.multiply(1., handle_mask=np.bitwise_or).mask # returns nothing, no mask, OK\r\n\r\n# multiply no mask by itself (no mask * no mask)\r\n>>> nref_nomask.multiply(nref_nomask, handle_mask=np.bitwise_or).mask # return nothing, no mask, OK\r\n\r\n# multiply mask by constant (mask * no mask)\r\n>>> nref_mask.multiply(1., handle_mask=np.bitwise_or).mask\r\n...\r\nTypeError: unsupported operand type(s) for |: 'int' and 'NoneType'\r\n\r\n# multiply mask by itself (mask * mask)\r\n>>> nref_mask.multiply(nref_mask, handle_mask=np.bitwise_or).mask\r\narray([[ 0, 1, 64],\r\n [ 8, 0, 1],\r\n [ 2, 1, 0]])\r\n\r\n# multiply mask by no mask (mask * no mask)\r\n>>> nref_mask.multiply(nref_nomask, handle_mask=np.bitwise_or).mask\r\n...\r\nTypeError: unsupported operand type(s) for |: 'int' and 'NoneType'\r\n```\r\n\n\n### Versions\n\n>>> import sys; print(\"Python\", sys.version)\r\nPython 3.10.11 | packaged by conda-forge | (main, May 10 2023, 19:07:22) [Clang 14.0.6 ]\r\n>>> import astropy; print(\"astropy\", astropy.__version__)\r\nastropy 5.3\r\n>>> import numpy; print(\"Numpy\", numpy.__version__)\r\nNumpy 1.24.3\r\n>>> import erfa; print(\"pyerfa\", erfa.__version__)\r\npyerfa 2.0.0.3\r\n>>> import scipy; print(\"Scipy\", scipy.__version__)\r\nScipy 1.10.1\r\n>>> import matplotlib; print(\"Matplotlib\", matplotlib.__version__)\r\nMatplotlib 3.7.1\r\n\n", "patch": "diff --git a/astropy/nddata/mixins/ndarithmetic.py b/astropy/nddata/mixins/ndarithmetic.py\n--- a/astropy/nddata/mixins/ndarithmetic.py\n+++ b/astropy/nddata/mixins/ndarithmetic.py\n@@ -520,10 +520,10 @@ def _arithmetic_mask(self, operation, operand, handle_mask, axis=None, **kwds):\n elif self.mask is None and operand is not None:\n # Make a copy so there is no reference in the result.\n return deepcopy(operand.mask)\n- elif operand is None:\n+ elif operand.mask is None:\n return deepcopy(self.mask)\n else:\n- # Now lets calculate the resulting mask (operation enforces copy)\n+ # Now let's calculate the resulting mask (operation enforces copy)\n return handle_mask(self.mask, operand.mask, **kwds)\n \n def _arithmetic_wcs(self, operation, operand, compare_wcs, **kwds):\n"} +{"instance_id": "astropy__astropy-6938", "file_changes": [{"file": "astropy/io/fits/fitsrec.py", "changes": {"edited_entities": ["astropy/io/fits/fitsrec.py:FITS_rec._scale_back_ascii"], "edited_modules": ["astropy/io/fits/fitsrec.py:FITS_rec"]}}], "repo": "astropy/astropy", "base_commit": "c76af9ed6bb89bfba45b9f5bc1e635188278e2fa", "problem_statement": "Possible bug in io.fits related to D exponents\nI came across the following code in ``fitsrec.py``:\r\n\r\n```python\r\n # Replace exponent separator in floating point numbers\r\n if 'D' in format:\r\n output_field.replace(encode_ascii('E'), encode_ascii('D'))\r\n```\r\n\r\nI think this may be incorrect because as far as I can tell ``replace`` is not an in-place operation for ``chararray`` (it returns a copy). Commenting out this code doesn't cause any tests to fail so I think this code isn't being tested anyway.\n", "patch": "diff --git a/astropy/io/fits/fitsrec.py b/astropy/io/fits/fitsrec.py\n--- a/astropy/io/fits/fitsrec.py\n+++ b/astropy/io/fits/fitsrec.py\n@@ -1261,7 +1261,7 @@ def _scale_back_ascii(self, col_idx, input_field, output_field):\n \n # Replace exponent separator in floating point numbers\n if 'D' in format:\n- output_field.replace(encode_ascii('E'), encode_ascii('D'))\n+ output_field[:] = output_field.replace(b'E', b'D')\n \n \n def _get_recarray_field(array, key):\n"} +{"instance_id": "astropy__astropy-7746", "file_changes": [{"file": "astropy/wcs/wcs.py", "changes": {"edited_entities": ["astropy/wcs/wcs.py:WCS._array_converter"], "edited_modules": ["astropy/wcs/wcs.py:WCS"]}}], "repo": "astropy/astropy", "base_commit": "d5bd3f68bb6d5ce3a61bdce9883ee750d1afade5", "problem_statement": "Issue when passing empty lists/arrays to WCS transformations\nThe following should not fail but instead should return empty lists/arrays:\r\n\r\n```\r\nIn [1]: from astropy.wcs import WCS\r\n\r\nIn [2]: wcs = WCS('2MASS_h.fits')\r\n\r\nIn [3]: wcs.wcs_pix2world([], [], 0)\r\n---------------------------------------------------------------------------\r\nInconsistentAxisTypesError Traceback (most recent call last)\r\n in ()\r\n----> 1 wcs.wcs_pix2world([], [], 0)\r\n\r\n~/Dropbox/Code/Astropy/astropy/astropy/wcs/wcs.py in wcs_pix2world(self, *args, **kwargs)\r\n 1352 return self._array_converter(\r\n 1353 lambda xy, o: self.wcs.p2s(xy, o)['world'],\r\n-> 1354 'output', *args, **kwargs)\r\n 1355 wcs_pix2world.__doc__ = \"\"\"\r\n 1356 Transforms pixel coordinates to world coordinates by doing\r\n\r\n~/Dropbox/Code/Astropy/astropy/astropy/wcs/wcs.py in _array_converter(self, func, sky, ra_dec_order, *args)\r\n 1267 \"a 1-D array for each axis, followed by an origin.\")\r\n 1268 \r\n-> 1269 return _return_list_of_arrays(axes, origin)\r\n 1270 \r\n 1271 raise TypeError(\r\n\r\n~/Dropbox/Code/Astropy/astropy/astropy/wcs/wcs.py in _return_list_of_arrays(axes, origin)\r\n 1223 if ra_dec_order and sky == 'input':\r\n 1224 xy = self._denormalize_sky(xy)\r\n-> 1225 output = func(xy, origin)\r\n 1226 if ra_dec_order and sky == 'output':\r\n 1227 output = self._normalize_sky(output)\r\n\r\n~/Dropbox/Code/Astropy/astropy/astropy/wcs/wcs.py in (xy, o)\r\n 1351 raise ValueError(\"No basic WCS settings were created.\")\r\n 1352 return self._array_converter(\r\n-> 1353 lambda xy, o: self.wcs.p2s(xy, o)['world'],\r\n 1354 'output', *args, **kwargs)\r\n 1355 wcs_pix2world.__doc__ = \"\"\"\r\n\r\nInconsistentAxisTypesError: ERROR 4 in wcsp2s() at line 2646 of file cextern/wcslib/C/wcs.c:\r\nncoord and/or nelem inconsistent with the wcsprm.\r\n```\n", "patch": "diff --git a/astropy/wcs/wcs.py b/astropy/wcs/wcs.py\n--- a/astropy/wcs/wcs.py\n+++ b/astropy/wcs/wcs.py\n@@ -1212,6 +1212,9 @@ def _array_converter(self, func, sky, *args, ra_dec_order=False):\n \"\"\"\n \n def _return_list_of_arrays(axes, origin):\n+ if any([x.size == 0 for x in axes]):\n+ return axes\n+\n try:\n axes = np.broadcast_arrays(*axes)\n except ValueError:\n@@ -1235,6 +1238,8 @@ def _return_single_array(xy, origin):\n raise ValueError(\n \"When providing two arguments, the array must be \"\n \"of shape (N, {0})\".format(self.naxis))\n+ if 0 in xy.shape:\n+ return xy\n if ra_dec_order and sky == 'input':\n xy = self._denormalize_sky(xy)\n result = func(xy, origin)\n"} +{"instance_id": "django__django-10914", "file_changes": [{"file": "django/conf/global_settings.py", "changes": {}}], "repo": "django/django", "base_commit": "e7fd69d051eaa67cb17f172a39b57253e9cb831a", "problem_statement": "Set default FILE_UPLOAD_PERMISSION to 0o644.\nDescription\n\t\nHello,\nAs far as I can see, the \u200bFile Uploads documentation page does not mention any permission issues.\nWhat I would like to see is a warning that in absence of explicitly configured FILE_UPLOAD_PERMISSIONS, the permissions for a file uploaded to FileSystemStorage might not be consistent depending on whether a MemoryUploadedFile or a TemporaryUploadedFile was used for temporary storage of the uploaded data (which, with the default FILE_UPLOAD_HANDLERS, in turn depends on the uploaded data size).\nThe tempfile.NamedTemporaryFile + os.rename sequence causes the resulting file permissions to be 0o0600 on some systems (I experience it here on CentOS 7.4.1708 and Python 3.6.5). In all probability, the implementation of Python's built-in tempfile module explicitly sets such permissions for temporary files due to security considerations.\nI found mentions of this issue \u200bon GitHub, but did not manage to find any existing bug report in Django's bug tracker.\n", "patch": "diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py\n--- a/django/conf/global_settings.py\n+++ b/django/conf/global_settings.py\n@@ -304,7 +304,7 @@ def gettext_noop(s):\n \n # The numeric mode to set newly-uploaded files to. The value should be a mode\n # you'd pass directly to os.chmod; see https://docs.python.org/library/os.html#files-and-directories.\n-FILE_UPLOAD_PERMISSIONS = None\n+FILE_UPLOAD_PERMISSIONS = 0o644\n \n # The numeric mode to assign to newly-created directories, when uploading files.\n # The value should be a mode as you'd pass to os.chmod;\n"} +{"instance_id": "django__django-10924", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {"edited_entities": ["django/db/models/fields/__init__.py:FilePathField.formfield"], "edited_modules": ["django/db/models/fields/__init__.py:FilePathField"]}}], "repo": "django/django", "base_commit": "bceadd2788dc2dad53eba0caae172bd8522fd483", "problem_statement": "Allow FilePathField path to accept a callable.\nDescription\n\t\nI have a special case where I want to create a model containing the path to some local files on the server/dev machine. Seeing as the place where these files are stored is different on different machines I have the following:\nimport os\nfrom django.conf import settings\nfrom django.db import models\nclass LocalFiles(models.Model):\n\tname = models.CharField(max_length=255)\n\tfile = models.FilePathField(path=os.path.join(settings.LOCAL_FILE_DIR, 'example_dir'))\nNow when running manage.py makemigrations it will resolve the path based on the machine it is being run on. Eg: /home//server_files/example_dir\nI had to manually change the migration to include the os.path.join() part to not break this when running the migration on production/other machine.\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -1709,7 +1709,7 @@ def get_prep_value(self, value):\n \n def formfield(self, **kwargs):\n return super().formfield(**{\n- 'path': self.path,\n+ 'path': self.path() if callable(self.path) else self.path,\n 'match': self.match,\n 'recursive': self.recursive,\n 'form_class': forms.FilePathField,\n"} +{"instance_id": "django__django-11001", "file_changes": [{"file": "django/db/models/sql/compiler.py", "changes": {"edited_entities": ["django/db/models/sql/compiler.py:SQLCompiler.__init__"], "edited_modules": ["django/db/models/sql/compiler.py:SQLCompiler"]}}], "repo": "django/django", "base_commit": "ef082ebb84f00e38af4e8880d04e8365c2766d34", "problem_statement": "Incorrect removal of order_by clause created as multiline RawSQL\nDescription\n\t\nHi.\nThe SQLCompiler is ripping off one of my \"order by\" clause, because he \"thinks\" the clause was already \"seen\" (in SQLCompiler.get_order_by()). I'm using expressions written as multiline RawSQLs, which are similar but not the same. \nThe bug is located in SQLCompiler.get_order_by(), somewhere around line computing part of SQL query without ordering:\nwithout_ordering = self.ordering_parts.search(sql).group(1)\nThe sql variable contains multiline sql. As a result, the self.ordering_parts regular expression is returning just a line containing ASC or DESC words. This line is added to seen set, and because my raw queries have identical last lines, only the first clasue is returing from SQLCompiler.get_order_by().\nAs a quick/temporal fix I can suggest making sql variable clean of newline characters, like this:\nsql_oneline = ' '.join(sql.split('\\n'))\nwithout_ordering = self.ordering_parts.search(sql_oneline).group(1)\nNote: beware of unicode (Py2.x u'') and EOL dragons (\\r).\nExample of my query:\n\treturn MyModel.objects.all().order_by(\n\t\tRawSQL('''\n\t\t\tcase when status in ('accepted', 'verification')\n\t\t\t\t then 2 else 1 end''', []).desc(),\n\t\tRawSQL('''\n\t\t\tcase when status in ('accepted', 'verification')\n\t\t\t\t then (accepted_datetime, preferred_datetime)\n\t\t\t\t else null end''', []).asc(),\n\t\tRawSQL('''\n\t\t\tcase when status not in ('accepted', 'verification')\n\t\t\t\t then (accepted_datetime, preferred_datetime, created_at)\n\t\t\t\t else null end''', []).desc())\nThe ordering_parts.search is returing accordingly:\n'\t\t\t\t then 2 else 1 end)'\n'\t\t\t\t else null end'\n'\t\t\t\t else null end'\nSecond RawSQL with a\t\t\t\t else null end part is removed from query.\nThe fun thing is that the issue can be solved by workaround by adding a space or any other char to the last line. \nSo in case of RawSQL I can just say, that current implementation of avoiding duplicates in order by clause works only for special/rare cases (or does not work in all cases). \nThe bug filed here is about wrong identification of duplicates (because it compares only last line of SQL passed to order by clause).\nHope my notes will help you fixing the issue. Sorry for my english.\n", "patch": "diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py\n--- a/django/db/models/sql/compiler.py\n+++ b/django/db/models/sql/compiler.py\n@@ -32,7 +32,8 @@ def __init__(self, query, connection, using):\n self.select = None\n self.annotation_col_map = None\n self.klass_info = None\n- self.ordering_parts = re.compile(r'(.*)\\s(ASC|DESC)(.*)')\n+ # Multiline ordering SQL clause may appear from RawSQL.\n+ self.ordering_parts = re.compile(r'^(.*)\\s(ASC|DESC)(.*)', re.MULTILINE | re.DOTALL)\n self._meta_ordering = None\n \n def setup_query(self):\n"} +{"instance_id": "django__django-11019", "file_changes": [{"file": "django/forms/widgets.py", "changes": {"edited_entities": ["django/forms/widgets.py:Media._css", "django/forms/widgets.py:Media._js", "django/forms/widgets.py:Media.merge"], "edited_modules": ["django/forms/widgets.py:Media"]}}], "repo": "django/django", "base_commit": "93e892bb645b16ebaf287beb5fe7f3ffe8d10408", "problem_statement": "Merging 3 or more media objects can throw unnecessary MediaOrderConflictWarnings\nDescription\n\t\nConsider the following form definition, where text-editor-extras.js depends on text-editor.js but all other JS files are independent:\nfrom django import forms\nclass ColorPicker(forms.Widget):\n\tclass Media:\n\t\tjs = ['color-picker.js']\nclass SimpleTextWidget(forms.Widget):\n\tclass Media:\n\t\tjs = ['text-editor.js']\nclass FancyTextWidget(forms.Widget):\n\tclass Media:\n\t\tjs = ['text-editor.js', 'text-editor-extras.js', 'color-picker.js']\nclass MyForm(forms.Form):\n\tbackground_color = forms.CharField(widget=ColorPicker())\n\tintro = forms.CharField(widget=SimpleTextWidget())\n\tbody = forms.CharField(widget=FancyTextWidget())\nDjango should be able to resolve the JS files for the final form into the order text-editor.js, text-editor-extras.js, color-picker.js. However, accessing MyForm().media results in:\n/projects/django/django/forms/widgets.py:145: MediaOrderConflictWarning: Detected duplicate Media files in an opposite order:\ntext-editor-extras.js\ntext-editor.js\n MediaOrderConflictWarning,\nMedia(css={}, js=['text-editor-extras.js', 'color-picker.js', 'text-editor.js'])\nThe MediaOrderConflictWarning is a result of the order that the additions happen in: ColorPicker().media + SimpleTextWidget().media produces Media(css={}, js=['color-picker.js', 'text-editor.js']), which (wrongly) imposes the constraint that color-picker.js must appear before text-editor.js.\nThe final result is particularly unintuitive here, as it's worse than the \"na\u00efve\" result produced by Django 1.11 before order-checking was added (color-picker.js, text-editor.js, text-editor-extras.js), and the pair of files reported in the warning message seems wrong too (aren't color-picker.js and text-editor.js the wrong-ordered ones?)\n", "patch": "diff --git a/django/forms/widgets.py b/django/forms/widgets.py\n--- a/django/forms/widgets.py\n+++ b/django/forms/widgets.py\n@@ -6,16 +6,21 @@\n import datetime\n import re\n import warnings\n+from collections import defaultdict\n from itertools import chain\n \n from django.conf import settings\n from django.forms.utils import to_current_timezone\n from django.templatetags.static import static\n from django.utils import datetime_safe, formats\n+from django.utils.datastructures import OrderedSet\n from django.utils.dates import MONTHS\n from django.utils.formats import get_format\n from django.utils.html import format_html, html_safe\n from django.utils.safestring import mark_safe\n+from django.utils.topological_sort import (\n+ CyclicDependencyError, stable_topological_sort,\n+)\n from django.utils.translation import gettext_lazy as _\n \n from .renderers import get_default_renderer\n@@ -59,22 +64,15 @@ def __str__(self):\n \n @property\n def _css(self):\n- css = self._css_lists[0]\n- # filter(None, ...) avoids calling merge with empty dicts.\n- for obj in filter(None, self._css_lists[1:]):\n- css = {\n- medium: self.merge(css.get(medium, []), obj.get(medium, []))\n- for medium in css.keys() | obj.keys()\n- }\n- return css\n+ css = defaultdict(list)\n+ for css_list in self._css_lists:\n+ for medium, sublist in css_list.items():\n+ css[medium].append(sublist)\n+ return {medium: self.merge(*lists) for medium, lists in css.items()}\n \n @property\n def _js(self):\n- js = self._js_lists[0]\n- # filter(None, ...) avoids calling merge() with empty lists.\n- for obj in filter(None, self._js_lists[1:]):\n- js = self.merge(js, obj)\n- return js\n+ return self.merge(*self._js_lists)\n \n def render(self):\n return mark_safe('\\n'.join(chain.from_iterable(getattr(self, 'render_' + name)() for name in MEDIA_TYPES)))\n@@ -115,39 +113,37 @@ def __getitem__(self, name):\n raise KeyError('Unknown media type \"%s\"' % name)\n \n @staticmethod\n- def merge(list_1, list_2):\n+ def merge(*lists):\n \"\"\"\n- Merge two lists while trying to keep the relative order of the elements.\n- Warn if the lists have the same two elements in a different relative\n- order.\n+ Merge lists while trying to keep the relative order of the elements.\n+ Warn if the lists have the same elements in a different relative order.\n \n For static assets it can be important to have them included in the DOM\n in a certain order. In JavaScript you may not be able to reference a\n global or in CSS you might want to override a style.\n \"\"\"\n- # Start with a copy of list_1.\n- combined_list = list(list_1)\n- last_insert_index = len(list_1)\n- # Walk list_2 in reverse, inserting each element into combined_list if\n- # it doesn't already exist.\n- for path in reversed(list_2):\n- try:\n- # Does path already exist in the list?\n- index = combined_list.index(path)\n- except ValueError:\n- # Add path to combined_list since it doesn't exist.\n- combined_list.insert(last_insert_index, path)\n- else:\n- if index > last_insert_index:\n- warnings.warn(\n- 'Detected duplicate Media files in an opposite order:\\n'\n- '%s\\n%s' % (combined_list[last_insert_index], combined_list[index]),\n- MediaOrderConflictWarning,\n- )\n- # path already exists in the list. Update last_insert_index so\n- # that the following elements are inserted in front of this one.\n- last_insert_index = index\n- return combined_list\n+ dependency_graph = defaultdict(set)\n+ all_items = OrderedSet()\n+ for list_ in filter(None, lists):\n+ head = list_[0]\n+ # The first items depend on nothing but have to be part of the\n+ # dependency graph to be included in the result.\n+ dependency_graph.setdefault(head, set())\n+ for item in list_:\n+ all_items.add(item)\n+ # No self dependencies\n+ if head != item:\n+ dependency_graph[item].add(head)\n+ head = item\n+ try:\n+ return stable_topological_sort(all_items, dependency_graph)\n+ except CyclicDependencyError:\n+ warnings.warn(\n+ 'Detected duplicate Media files in an opposite order: {}'.format(\n+ ', '.join(repr(l) for l in lists)\n+ ), MediaOrderConflictWarning,\n+ )\n+ return list(all_items)\n \n def __add__(self, other):\n combined = Media()\n"} +{"instance_id": "django__django-11039", "file_changes": [{"file": "django/core/management/commands/sqlmigrate.py", "changes": {"edited_entities": ["django/core/management/commands/sqlmigrate.py:Command.handle"], "edited_modules": ["django/core/management/commands/sqlmigrate.py:Command"]}}], "repo": "django/django", "base_commit": "d5276398046ce4a102776a1e67dcac2884d80dfe", "problem_statement": "sqlmigrate wraps it's outpout in BEGIN/COMMIT even if the database doesn't support transactional DDL\nDescription\n\t \n\t\t(last modified by Simon Charette)\n\t \nThe migration executor only adds the outer BEGIN/COMMIT \u200bif the migration is atomic and \u200bthe schema editor can rollback DDL but the current sqlmigrate logic only takes migration.atomic into consideration.\nThe issue can be addressed by\nChanging sqlmigrate \u200bassignment of self.output_transaction to consider connection.features.can_rollback_ddl as well.\nAdding a test in tests/migrations/test_commands.py based on \u200ban existing test for non-atomic migrations that mocks connection.features.can_rollback_ddl to False instead of overdidding MIGRATION_MODULES to point to a non-atomic migration.\nI marked the ticket as easy picking because I included the above guidelines but feel free to uncheck it if you deem it inappropriate.\n", "patch": "diff --git a/django/core/management/commands/sqlmigrate.py b/django/core/management/commands/sqlmigrate.py\n--- a/django/core/management/commands/sqlmigrate.py\n+++ b/django/core/management/commands/sqlmigrate.py\n@@ -55,8 +55,9 @@ def handle(self, *args, **options):\n migration_name, app_label))\n targets = [(app_label, migration.name)]\n \n- # Show begin/end around output only for atomic migrations\n- self.output_transaction = migration.atomic\n+ # Show begin/end around output for atomic migrations, if the database\n+ # supports transactional DDL.\n+ self.output_transaction = migration.atomic and connection.features.can_rollback_ddl\n \n # Make a plan that represents just the requested migrations and show SQL\n # for it\n"} +{"instance_id": "django__django-11049", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {}}], "repo": "django/django", "base_commit": "17455e924e243e7a55e8a38f45966d8cbb27c273", "problem_statement": "Correct expected format in invalid DurationField error message\nDescription\n\t\nIf you enter a duration \"14:00\" into a duration field, it translates to \"00:14:00\" which is 14 minutes.\nThe current error message for invalid DurationField says that this should be the format of durations: \"[DD] [HH:[MM:]]ss[.uuuuuu]\". But according to the actual behaviour, it should be: \"[DD] [[HH:]MM:]ss[.uuuuuu]\", because seconds are mandatory, minutes are optional, and hours are optional if minutes are provided.\nThis seems to be a mistake in all Django versions that support the DurationField.\nAlso the duration fields could have a default help_text with the requested format, because the syntax is not self-explanatory.\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -1587,7 +1587,7 @@ class DurationField(Field):\n empty_strings_allowed = False\n default_error_messages = {\n 'invalid': _(\"'%(value)s' value has an invalid format. It must be in \"\n- \"[DD] [HH:[MM:]]ss[.uuuuuu] format.\")\n+ \"[DD] [[HH:]MM:]ss[.uuuuuu] format.\")\n }\n description = _(\"Duration\")\n \n"} +{"instance_id": "django__django-11099", "file_changes": [{"file": "django/contrib/auth/validators.py", "changes": {}}], "repo": "django/django", "base_commit": "d26b2424437dabeeca94d7900b37d2df4410da0c", "problem_statement": "UsernameValidator allows trailing newline in usernames\nDescription\n\t\nASCIIUsernameValidator and UnicodeUsernameValidator use the regex \nr'^[\\w.@+-]+$'\nThe intent is to only allow alphanumeric characters as well as ., @, +, and -. However, a little known quirk of Python regexes is that $ will also match a trailing newline. Therefore, the user name validators will accept usernames which end with a newline. You can avoid this behavior by instead using \\A and \\Z to terminate regexes. For example, the validator regex could be changed to\nr'\\A[\\w.@+-]+\\Z'\nin order to reject usernames that end with a newline.\nI am not sure how to officially post a patch, but the required change is trivial - using the regex above in the two validators in contrib.auth.validators.\n", "patch": "diff --git a/django/contrib/auth/validators.py b/django/contrib/auth/validators.py\n--- a/django/contrib/auth/validators.py\n+++ b/django/contrib/auth/validators.py\n@@ -7,7 +7,7 @@\n \n @deconstructible\n class ASCIIUsernameValidator(validators.RegexValidator):\n- regex = r'^[\\w.@+-]+$'\n+ regex = r'^[\\w.@+-]+\\Z'\n message = _(\n 'Enter a valid username. This value may contain only English letters, '\n 'numbers, and @/./+/-/_ characters.'\n@@ -17,7 +17,7 @@ class ASCIIUsernameValidator(validators.RegexValidator):\n \n @deconstructible\n class UnicodeUsernameValidator(validators.RegexValidator):\n- regex = r'^[\\w.@+-]+$'\n+ regex = r'^[\\w.@+-]+\\Z'\n message = _(\n 'Enter a valid username. This value may contain only letters, '\n 'numbers, and @/./+/-/_ characters.'\n"} +{"instance_id": "django__django-11133", "file_changes": [{"file": "django/http/response.py", "changes": {"edited_entities": ["django/http/response.py:HttpResponseBase.make_bytes"], "edited_modules": ["django/http/response.py:HttpResponseBase"]}}], "repo": "django/django", "base_commit": "879cc3da6249e920b8d54518a0ae06de835d7373", "problem_statement": "HttpResponse doesn't handle memoryview objects\nDescription\n\t\nI am trying to write a BinaryField retrieved from the database into a HttpResponse. When the database is Sqlite this works correctly, but Postgresql returns the contents of the field as a memoryview object and it seems like current Django doesn't like this combination:\nfrom django.http import HttpResponse\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \n# String content\nresponse = HttpResponse(\"My Content\")\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\nresponse.content\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \n# Out: b'My Content'\n# This is correct\n# Bytes content\nresponse = HttpResponse(b\"My Content\")\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \nresponse.content\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \n# Out: b'My Content'\n# This is also correct\n# memoryview content\nresponse = HttpResponse(memoryview(b\"My Content\"))\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \nresponse.content\n# Out: b''\n# This is not correct, I am expecting b'My Content'\n", "patch": "diff --git a/django/http/response.py b/django/http/response.py\n--- a/django/http/response.py\n+++ b/django/http/response.py\n@@ -229,7 +229,7 @@ def make_bytes(self, value):\n # Handle string types -- we can't rely on force_bytes here because:\n # - Python attempts str conversion first\n # - when self._charset != 'utf-8' it re-encodes the content\n- if isinstance(value, bytes):\n+ if isinstance(value, (bytes, memoryview)):\n return bytes(value)\n if isinstance(value, str):\n return bytes(value.encode(self.charset))\n"} +{"instance_id": "django__django-11179", "file_changes": [{"file": "django/db/models/deletion.py", "changes": {"edited_entities": ["django/db/models/deletion.py:Collector.delete"], "edited_modules": ["django/db/models/deletion.py:Collector"]}}], "repo": "django/django", "base_commit": "19fc6376ce67d01ca37a91ef2f55ef769f50513a", "problem_statement": "delete() on instances of models without any dependencies doesn't clear PKs.\nDescription\n\t\nDeleting any model with no dependencies not updates the PK on the model. It should be set to None after .delete() call.\nSee Django.db.models.deletion:276-281. Should update the model line 280.\n", "patch": "diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py\n--- a/django/db/models/deletion.py\n+++ b/django/db/models/deletion.py\n@@ -277,6 +277,7 @@ def delete(self):\n if self.can_fast_delete(instance):\n with transaction.mark_for_rollback_on_error():\n count = sql.DeleteQuery(model).delete_batch([instance.pk], self.using)\n+ setattr(instance, model._meta.pk.attname, None)\n return count, {model._meta.label: count}\n \n with transaction.atomic(using=self.using, savepoint=False):\n"} +{"instance_id": "django__django-11283", "file_changes": [{"file": "django/contrib/auth/migrations/0011_update_proxy_permissions.py", "changes": {"edited_entities": ["django/contrib/auth/migrations/0011_update_proxy_permissions.py:update_proxy_model_permissions"], "edited_modules": ["django/contrib/auth/migrations/0011_update_proxy_permissions.py:update_proxy_model_permissions"]}}], "repo": "django/django", "base_commit": "08a4ee06510ae45562c228eefbdcaac84bd38c7a", "problem_statement": "Migration auth.0011_update_proxy_permissions fails for models recreated as a proxy.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \nI am trying to update my project to Django 2.2. When I launch python manage.py migrate, I get this error message when migration auth.0011_update_proxy_permissions is applying (full stacktrace is available \u200bhere):\ndjango.db.utils.IntegrityError: duplicate key value violates unique constraint \"idx_18141_auth_permission_content_type_id_01ab375a_uniq\" DETAIL: Key (co.ntent_type_id, codename)=(12, add_agency) already exists.\nIt looks like the migration is trying to re-create already existing entries in the auth_permission table. At first I though it cloud because we recently renamed a model. But after digging and deleting the entries associated with the renamed model from our database in the auth_permission table, the problem still occurs with other proxy models.\nI tried to update directly from 2.0.13 and 2.1.8. The issues appeared each time. I also deleted my venv and recreated it without an effect.\nI searched for a ticket about this on the bug tracker but found nothing. I also posted this on \u200bdjango-users and was asked to report this here.\n", "patch": "diff --git a/django/contrib/auth/migrations/0011_update_proxy_permissions.py b/django/contrib/auth/migrations/0011_update_proxy_permissions.py\n--- a/django/contrib/auth/migrations/0011_update_proxy_permissions.py\n+++ b/django/contrib/auth/migrations/0011_update_proxy_permissions.py\n@@ -1,5 +1,18 @@\n-from django.db import migrations\n+import sys\n+\n+from django.core.management.color import color_style\n+from django.db import migrations, transaction\n from django.db.models import Q\n+from django.db.utils import IntegrityError\n+\n+WARNING = \"\"\"\n+ A problem arose migrating proxy model permissions for {old} to {new}.\n+\n+ Permission(s) for {new} already existed.\n+ Codenames Q: {query}\n+\n+ Ensure to audit ALL permissions for {old} and {new}.\n+\"\"\"\n \n \n def update_proxy_model_permissions(apps, schema_editor, reverse=False):\n@@ -7,6 +20,7 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False):\n Update the content_type of proxy model permissions to use the ContentType\n of the proxy model.\n \"\"\"\n+ style = color_style()\n Permission = apps.get_model('auth', 'Permission')\n ContentType = apps.get_model('contenttypes', 'ContentType')\n for Model in apps.get_models():\n@@ -24,10 +38,16 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False):\n proxy_content_type = ContentType.objects.get_for_model(Model, for_concrete_model=False)\n old_content_type = proxy_content_type if reverse else concrete_content_type\n new_content_type = concrete_content_type if reverse else proxy_content_type\n- Permission.objects.filter(\n- permissions_query,\n- content_type=old_content_type,\n- ).update(content_type=new_content_type)\n+ try:\n+ with transaction.atomic():\n+ Permission.objects.filter(\n+ permissions_query,\n+ content_type=old_content_type,\n+ ).update(content_type=new_content_type)\n+ except IntegrityError:\n+ old = '{}_{}'.format(old_content_type.app_label, old_content_type.model)\n+ new = '{}_{}'.format(new_content_type.app_label, new_content_type.model)\n+ sys.stdout.write(style.WARNING(WARNING.format(old=old, new=new, query=permissions_query)))\n \n \n def revert_proxy_model_permissions(apps, schema_editor):\n"} +{"instance_id": "django__django-11422", "file_changes": [{"file": "django/utils/autoreload.py", "changes": {"edited_entities": ["django/utils/autoreload.py:iter_modules_and_files"], "edited_modules": ["django/utils/autoreload.py:iter_modules_and_files"]}}], "repo": "django/django", "base_commit": "df46b329e0900e9e4dc1d60816c1dce6dfc1094e", "problem_statement": "Autoreloader with StatReloader doesn't track changes in manage.py.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \nThis is a bit convoluted, but here we go.\nEnvironment (OSX 10.11):\n$ python -V\nPython 3.6.2\n$ pip -V\npip 19.1.1\n$ pip install Django==2.2.1\nSteps to reproduce:\nRun a server python manage.py runserver\nEdit the manage.py file, e.g. add print(): \ndef main():\n\tprint('sth')\n\tos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ticket_30479.settings')\n\t...\nUnder 2.1.8 (and prior), this will trigger the auto-reloading mechanism. Under 2.2.1, it won't. As far as I can tell from the django.utils.autoreload log lines, it never sees the manage.py itself.\n", "patch": "diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py\n--- a/django/utils/autoreload.py\n+++ b/django/utils/autoreload.py\n@@ -114,7 +114,15 @@ def iter_modules_and_files(modules, extra_files):\n # During debugging (with PyDev) the 'typing.io' and 'typing.re' objects\n # are added to sys.modules, however they are types not modules and so\n # cause issues here.\n- if not isinstance(module, ModuleType) or getattr(module, '__spec__', None) is None:\n+ if not isinstance(module, ModuleType):\n+ continue\n+ if module.__name__ == '__main__':\n+ # __main__ (usually manage.py) doesn't always have a __spec__ set.\n+ # Handle this by falling back to using __file__, resolved below.\n+ # See https://docs.python.org/reference/import.html#main-spec\n+ sys_file_paths.append(module.__file__)\n+ continue\n+ if getattr(module, '__spec__', None) is None:\n continue\n spec = module.__spec__\n # Modules could be loaded from places without a concrete location. If\n"} +{"instance_id": "django__django-11564", "file_changes": [{"file": "django/conf/__init__.py", "changes": {"added_entities": ["django/conf/__init__.py:LazySettings._add_script_prefix", "django/conf/__init__.py:LazySettings.STATIC_URL", "django/conf/__init__.py:LazySettings.MEDIA_URL"], "added_modules": ["django/conf/__init__.py:LazySettings"]}}], "repo": "django/django", "base_commit": "580e644f24f1c5ae5b94784fb73a9953a178fd26", "problem_statement": "Add support for SCRIPT_NAME in STATIC_URL and MEDIA_URL\nDescription\n\t \n\t\t(last modified by Rostyslav Bryzgunov)\n\t \nBy default, {% static '...' %} tag just appends STATIC_URL in the path. When running on sub-path, using SCRIPT_NAME WSGI param, it results in incorrect static URL - it doesn't prepend SCRIPT_NAME prefix.\nThis problem can be solved with prepending SCRIPT_NAME to STATIC_URL in settings.py but that doesn't work when SCRIPT_NAME is a dynamic value.\nThis can be easily added into default Django static tag and django.contrib.staticfiles tag as following:\ndef render(self, context):\n\turl = self.url(context)\n\t# Updating url here with request.META['SCRIPT_NAME'] \n\tif self.varname is None:\n\t\treturn url\n\tcontext[self.varname] = url\n\t\treturn ''\nOn more research I found that FileSystemStorage and StaticFilesStorage ignores SCRIPT_NAME as well. \nWe might have to do a lot of changes but I think it's worth the efforts.\n", "patch": "diff --git a/django/conf/__init__.py b/django/conf/__init__.py\n--- a/django/conf/__init__.py\n+++ b/django/conf/__init__.py\n@@ -15,7 +15,8 @@\n \n import django\n from django.conf import global_settings\n-from django.core.exceptions import ImproperlyConfigured\n+from django.core.exceptions import ImproperlyConfigured, ValidationError\n+from django.core.validators import URLValidator\n from django.utils.deprecation import RemovedInDjango40Warning\n from django.utils.functional import LazyObject, empty\n \n@@ -109,6 +110,26 @@ def configure(self, default_settings=global_settings, **options):\n setattr(holder, name, value)\n self._wrapped = holder\n \n+ @staticmethod\n+ def _add_script_prefix(value):\n+ \"\"\"\n+ Add SCRIPT_NAME prefix to relative paths.\n+\n+ Useful when the app is being served at a subpath and manually prefixing\n+ subpath to STATIC_URL and MEDIA_URL in settings is inconvenient.\n+ \"\"\"\n+ # Don't apply prefix to valid URLs.\n+ try:\n+ URLValidator()(value)\n+ return value\n+ except (ValidationError, AttributeError):\n+ pass\n+ # Don't apply prefix to absolute paths.\n+ if value.startswith('/'):\n+ return value\n+ from django.urls import get_script_prefix\n+ return '%s%s' % (get_script_prefix(), value)\n+\n @property\n def configured(self):\n \"\"\"Return True if the settings have already been configured.\"\"\"\n@@ -128,6 +149,14 @@ def PASSWORD_RESET_TIMEOUT_DAYS(self):\n )\n return self.__getattr__('PASSWORD_RESET_TIMEOUT_DAYS')\n \n+ @property\n+ def STATIC_URL(self):\n+ return self._add_script_prefix(self.__getattr__('STATIC_URL'))\n+\n+ @property\n+ def MEDIA_URL(self):\n+ return self._add_script_prefix(self.__getattr__('MEDIA_URL'))\n+\n \n class Settings:\n def __init__(self, settings_module):\n"} +{"instance_id": "django__django-11583", "file_changes": [{"file": "django/utils/autoreload.py", "changes": {"edited_entities": ["django/utils/autoreload.py:iter_modules_and_files"], "edited_modules": ["django/utils/autoreload.py:iter_modules_and_files"]}}], "repo": "django/django", "base_commit": "60dc957a825232fdda9138e2f8878b2ca407a7c9", "problem_statement": "Auto-reloading with StatReloader very intermittently throws \"ValueError: embedded null byte\".\nDescription\n\t\nRaising this mainly so that it's tracked, as I have no idea how to reproduce it, nor why it's happening. It ultimately looks like a problem with Pathlib, which wasn't used prior to 2.2.\nStacktrace:\nTraceback (most recent call last):\n File \"manage.py\" ...\n\texecute_from_command_line(sys.argv)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/__init__.py\", line 381, in execute_from_command_line\n\tutility.execute()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/__init__.py\", line 375, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/base.py\", line 323, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 60, in execute\n\tsuper().execute(*args, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/base.py\", line 364, in execute\n\toutput = self.handle(*args, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 95, in handle\n\tself.run(**options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/core/management/commands/runserver.py\", line 102, in run\n\tautoreload.run_with_reloader(self.inner_run, **options)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 577, in run_with_reloader\n\tstart_django(reloader, main_func, *args, **kwargs)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 562, in start_django\n\treloader.run(django_main_thread)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 280, in run\n\tself.run_loop()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 286, in run_loop\n\tnext(ticker)\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 326, in tick\n\tfor filepath, mtime in self.snapshot_files():\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 342, in snapshot_files\n\tfor file in self.watched_files():\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 241, in watched_files\n\tyield from iter_all_python_module_files()\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 103, in iter_all_python_module_files\n\treturn iter_modules_and_files(modules, frozenset(_error_files))\n File \"/Userz/kez/path/to/venv/lib/python3.6/site-packages/django/utils/autoreload.py\", line 132, in iter_modules_and_files\n\tresults.add(path.resolve().absolute())\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 1120, in resolve\n\ts = self._flavour.resolve(self, strict=strict)\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 346, in resolve\n\treturn _resolve(base, str(path)) or sep\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 330, in _resolve\n\ttarget = accessor.readlink(newpath)\n File \"/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/pathlib.py\", line 441, in readlink\n\treturn os.readlink(path)\nValueError: embedded null byte\nI did print(path) before os.readlink(path) in pathlib and ended up with:\n/Users/kez\n/Users/kez/.pyenv\n/Users/kez/.pyenv/versions\n/Users/kez/.pyenv/versions/3.6.2\n/Users/kez/.pyenv/versions/3.6.2/lib\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/asyncio\n/Users/kez/.pyenv/versions/3.6.2/lib/python3.6/asyncio/selector_events.py\n/Users\nIt always seems to be /Users which is last\nIt may have already printed /Users as part of another .resolve() multiple times (that is, the order is not deterministic, and it may have traversed beyond /Users successfully many times during startup.\nI don't know where to begin looking for the rogue null byte, nor why it only exists sometimes.\nBest guess I have is that there's a mountpoint in /Users to a samba share which may not have been connected to yet? I dunno.\nI have no idea if it's fixable without removing the use of pathlib (which tbh I think should happen anyway, because it's slow) and reverting to using os.path.join and friends. \nI have no idea if it's fixed in a later Python version, but with no easy way to reproduce ... dunno how I'd check.\nI have no idea if it's something specific to my system (pyenv, OSX 10.11, etc)\n", "patch": "diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py\n--- a/django/utils/autoreload.py\n+++ b/django/utils/autoreload.py\n@@ -143,6 +143,10 @@ def iter_modules_and_files(modules, extra_files):\n # The module could have been removed, don't fail loudly if this\n # is the case.\n continue\n+ except ValueError as e:\n+ # Network filesystems may return null bytes in file paths.\n+ logger.debug('\"%s\" raised when resolving path: \"%s\"' % (str(e), path))\n+ continue\n results.add(resolved_path)\n return frozenset(results)\n \n"} +{"instance_id": "django__django-11620", "file_changes": [{"file": "django/views/debug.py", "changes": {"edited_entities": ["django/views/debug.py:technical_404_response"], "edited_modules": ["django/views/debug.py:technical_404_response"]}}], "repo": "django/django", "base_commit": "514efa3129792ec2abb2444f3e7aeb3f21a38386", "problem_statement": "When DEBUG is True, raising Http404 in a path converter's to_python method does not result in a technical response\nDescription\n\t\nThis is the response I get (plain text): \nA server error occurred. Please contact the administrator.\nI understand a ValueError should be raised which tells the URL resolver \"this path does not match, try next one\" but Http404 is what came to my mind intuitively and the error message was not very helpful.\nOne could also make a point that raising a Http404 should be valid way to tell the resolver \"this is indeed the right path but the current parameter value does not match anything so stop what you are doing and let the handler return the 404 page (including a helpful error message when DEBUG is True instead of the default 'Django tried these URL patterns')\".\nThis would prove useful for example to implement a path converter that uses get_object_or_404.\n", "patch": "diff --git a/django/views/debug.py b/django/views/debug.py\n--- a/django/views/debug.py\n+++ b/django/views/debug.py\n@@ -5,10 +5,10 @@\n from pathlib import Path\n \n from django.conf import settings\n-from django.http import HttpResponse, HttpResponseNotFound\n+from django.http import Http404, HttpResponse, HttpResponseNotFound\n from django.template import Context, Engine, TemplateDoesNotExist\n from django.template.defaultfilters import pprint\n-from django.urls import Resolver404, resolve\n+from django.urls import resolve\n from django.utils import timezone\n from django.utils.datastructures import MultiValueDict\n from django.utils.encoding import force_str\n@@ -483,7 +483,7 @@ def technical_404_response(request, exception):\n caller = ''\n try:\n resolver_match = resolve(request.path)\n- except Resolver404:\n+ except Http404:\n pass\n else:\n obj = resolver_match.func\n"} +{"instance_id": "django__django-11630", "file_changes": [{"file": "django/core/checks/model_checks.py", "changes": {"edited_entities": ["django/core/checks/model_checks.py:check_all_models"], "edited_modules": ["django/core/checks/model_checks.py:check_all_models"]}}], "repo": "django/django", "base_commit": "65e86948b80262574058a94ccaae3a9b59c3faea", "problem_statement": "Django throws error when different apps with different models have the same name table name.\nDescription\n\t\nError message:\ntable_name: (models.E028) db_table 'table_name' is used by multiple models: base.ModelName, app2.ModelName.\nWe have a Base app that points to a central database and that has its own tables. We then have multiple Apps that talk to their own databases. Some share the same table names.\nWe have used this setup for a while, but after upgrading to Django 2.2 we're getting an error saying we're not allowed 2 apps, with 2 different models to have the same table names. \nIs this correct behavior? We've had to roll back to Django 2.0 for now.\n", "patch": "diff --git a/django/core/checks/model_checks.py b/django/core/checks/model_checks.py\n--- a/django/core/checks/model_checks.py\n+++ b/django/core/checks/model_checks.py\n@@ -4,7 +4,8 @@\n from itertools import chain\n \n from django.apps import apps\n-from django.core.checks import Error, Tags, register\n+from django.conf import settings\n+from django.core.checks import Error, Tags, Warning, register\n \n \n @register(Tags.models)\n@@ -35,14 +36,25 @@ def check_all_models(app_configs=None, **kwargs):\n indexes[model_index.name].append(model._meta.label)\n for model_constraint in model._meta.constraints:\n constraints[model_constraint.name].append(model._meta.label)\n+ if settings.DATABASE_ROUTERS:\n+ error_class, error_id = Warning, 'models.W035'\n+ error_hint = (\n+ 'You have configured settings.DATABASE_ROUTERS. Verify that %s '\n+ 'are correctly routed to separate databases.'\n+ )\n+ else:\n+ error_class, error_id = Error, 'models.E028'\n+ error_hint = None\n for db_table, model_labels in db_table_models.items():\n if len(model_labels) != 1:\n+ model_labels_str = ', '.join(model_labels)\n errors.append(\n- Error(\n+ error_class(\n \"db_table '%s' is used by multiple models: %s.\"\n- % (db_table, ', '.join(db_table_models[db_table])),\n+ % (db_table, model_labels_str),\n obj=db_table,\n- id='models.E028',\n+ hint=(error_hint % model_labels_str) if error_hint else None,\n+ id=error_id,\n )\n )\n for index_name, model_labels in indexes.items():\n"} +{"instance_id": "django__django-11742", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {"edited_entities": ["django/db/models/fields/__init__.py:Field._check_choices"], "edited_modules": ["django/db/models/fields/__init__.py:Field"]}}], "repo": "django/django", "base_commit": "fee75d2aed4e58ada6567c464cfd22e89dc65f4a", "problem_statement": "Add check to ensure max_length fits longest choice.\nDescription\n\t\nThere is currently no check to ensure that Field.max_length is large enough to fit the longest value in Field.choices.\nThis would be very helpful as often this mistake is not noticed until an attempt is made to save a record with those values that are too long.\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -257,6 +257,7 @@ def is_value(value, accept_promise=True):\n )\n ]\n \n+ choice_max_length = 0\n # Expect [group_name, [value, display]]\n for choices_group in self.choices:\n try:\n@@ -270,16 +271,32 @@ def is_value(value, accept_promise=True):\n for value, human_name in group_choices\n ):\n break\n+ if self.max_length is not None and group_choices:\n+ choice_max_length = max(\n+ choice_max_length,\n+ *(len(value) for value, _ in group_choices if isinstance(value, str)),\n+ )\n except (TypeError, ValueError):\n # No groups, choices in the form [value, display]\n value, human_name = group_name, group_choices\n if not is_value(value) or not is_value(human_name):\n break\n+ if self.max_length is not None and isinstance(value, str):\n+ choice_max_length = max(choice_max_length, len(value))\n \n # Special case: choices=['ab']\n if isinstance(choices_group, str):\n break\n else:\n+ if self.max_length is not None and choice_max_length > self.max_length:\n+ return [\n+ checks.Error(\n+ \"'max_length' is too small to fit the longest value \"\n+ \"in 'choices' (%d characters).\" % choice_max_length,\n+ obj=self,\n+ id='fields.E009',\n+ ),\n+ ]\n return []\n \n return [\n"} +{"instance_id": "django__django-11797", "file_changes": [{"file": "django/db/models/lookups.py", "changes": {"edited_entities": ["django/db/models/lookups.py:Exact.process_rhs"], "edited_modules": ["django/db/models/lookups.py:Exact"]}}], "repo": "django/django", "base_commit": "3346b78a8a872286a245d1e77ef4718fc5e6be1a", "problem_statement": "Filtering on query result overrides GROUP BY of internal query\nDescription\n\t\nfrom django.contrib.auth import models\na = models.User.objects.filter(email__isnull=True).values('email').annotate(m=Max('id')).values('m')\nprint(a.query) # good\n# SELECT MAX(\"auth_user\".\"id\") AS \"m\" FROM \"auth_user\" WHERE \"auth_user\".\"email\" IS NULL GROUP BY \"auth_user\".\"email\"\nprint(a[:1].query) # good\n# SELECT MAX(\"auth_user\".\"id\") AS \"m\" FROM \"auth_user\" WHERE \"auth_user\".\"email\" IS NULL GROUP BY \"auth_user\".\"email\" LIMIT 1\nb = models.User.objects.filter(id=a[:1])\nprint(b.query) # GROUP BY U0.\"id\" should be GROUP BY U0.\"email\"\n# SELECT ... FROM \"auth_user\" WHERE \"auth_user\".\"id\" = (SELECT U0.\"id\" FROM \"auth_user\" U0 WHERE U0.\"email\" IS NULL GROUP BY U0.\"id\" LIMIT 1)\n", "patch": "diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py\n--- a/django/db/models/lookups.py\n+++ b/django/db/models/lookups.py\n@@ -262,9 +262,9 @@ def process_rhs(self, compiler, connection):\n from django.db.models.sql.query import Query\n if isinstance(self.rhs, Query):\n if self.rhs.has_limit_one():\n- # The subquery must select only the pk.\n- self.rhs.clear_select_clause()\n- self.rhs.add_fields(['pk'])\n+ if not self.rhs.has_select_fields:\n+ self.rhs.clear_select_clause()\n+ self.rhs.add_fields(['pk'])\n else:\n raise ValueError(\n 'The QuerySet value for an exact lookup must be limited to '\n"} +{"instance_id": "django__django-11815", "file_changes": [{"file": "django/db/migrations/serializer.py", "changes": {"edited_entities": ["django/db/migrations/serializer.py:EnumSerializer.serialize"], "edited_modules": ["django/db/migrations/serializer.py:EnumSerializer"]}}], "repo": "django/django", "base_commit": "e02f67ef2d03d48128e7a118bf75f0418e24e8ac", "problem_statement": "Migrations uses value of enum object instead of its name.\nDescription\n\t \n\t\t(last modified by oasl)\n\t \nWhen using Enum object as a default value for a CharField, the generated migration file uses the value of the Enum object instead of the its name. This causes a problem when using Django translation on the value of the Enum object. \nThe problem is that, when the Enum object value get translated to the users language, the old migration files raise an error stating that the Enum does not have the corresponding value. (because the Enum value is translated to another language)\nExample:\nLet say we have this code in models.py:\nfrom enum import Enum\nfrom django.utils.translation import gettext_lazy as _\nfrom django.db import models\nclass Status(Enum):\n\tGOOD = _('Good') # 'Good' will be translated\n\tBAD = _('Bad') # 'Bad' will be translated\n\tdef __str__(self):\n\t\treturn self.name\nclass Item(models.Model):\n\tstatus = models.CharField(default=Status.GOOD, max_length=128)\nIn the generated migration file, the code will be:\n...\n('status', models.CharField(default=Status('Good'), max_length=128))\n...\nAfter the translation, 'Good' will be translated to another word and it will not be part of the Status Enum class any more, so the migration file will raise the error on the previous line:\nValueError: 'Good' is not a valid Status\nShouldn't the code generated by the migration uses the name of the Status Enum 'GOOD', not the value of it, since it is changeable?\nIt should be:\n('status', models.CharField(default=Status['GOOD'], max_length=128))\nThis will be correct regardless of the translated word\n", "patch": "diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py\n--- a/django/db/migrations/serializer.py\n+++ b/django/db/migrations/serializer.py\n@@ -120,9 +120,10 @@ class EnumSerializer(BaseSerializer):\n def serialize(self):\n enum_class = self.value.__class__\n module = enum_class.__module__\n- v_string, v_imports = serializer_factory(self.value.value).serialize()\n- imports = {'import %s' % module, *v_imports}\n- return \"%s.%s(%s)\" % (module, enum_class.__name__, v_string), imports\n+ return (\n+ '%s.%s[%r]' % (module, enum_class.__name__, self.value.name),\n+ {'import %s' % module},\n+ )\n \n \n class FloatSerializer(BaseSimpleSerializer):\n"} +{"instance_id": "django__django-11848", "file_changes": [{"file": "django/utils/http.py", "changes": {"edited_entities": ["django/utils/http.py:parse_http_date"], "edited_modules": ["django/utils/http.py:parse_http_date"]}}], "repo": "django/django", "base_commit": "f0adf3b9b7a19cdee05368ff0c0c2d087f011180", "problem_statement": "django.utils.http.parse_http_date two digit year check is incorrect\nDescription\n\t \n\t\t(last modified by Ad Timmering)\n\t \nRFC 850 does not mention this, but in RFC 7231 (and there's something similar in RFC 2822), there's the following quote:\nRecipients of a timestamp value in rfc850-date format, which uses a\ntwo-digit year, MUST interpret a timestamp that appears to be more\nthan 50 years in the future as representing the most recent year in\nthe past that had the same last two digits.\nCurrent logic is hard coded to consider 0-69 to be in 2000-2069, and 70-99 to be 1970-1999, instead of comparing versus the current year.\n", "patch": "diff --git a/django/utils/http.py b/django/utils/http.py\n--- a/django/utils/http.py\n+++ b/django/utils/http.py\n@@ -176,10 +176,14 @@ def parse_http_date(date):\n try:\n year = int(m.group('year'))\n if year < 100:\n- if year < 70:\n- year += 2000\n+ current_year = datetime.datetime.utcnow().year\n+ current_century = current_year - (current_year % 100)\n+ if year - (current_year % 100) > 50:\n+ # year that appears to be more than 50 years in the future are\n+ # interpreted as representing the past.\n+ year += current_century - 100\n else:\n- year += 1900\n+ year += current_century\n month = MONTHS.index(m.group('mon').lower()) + 1\n day = int(m.group('day'))\n hour = int(m.group('hour'))\n"} +{"instance_id": "django__django-11905", "file_changes": [{"file": "django/db/models/lookups.py", "changes": {"edited_entities": ["django/db/models/lookups.py:IsNull.as_sql"], "edited_modules": ["django/db/models/lookups.py:IsNull"]}}], "repo": "django/django", "base_commit": "2f72480fbd27896c986c45193e1603e35c0b19a7", "problem_statement": "Prevent using __isnull lookup with non-boolean value.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \n__isnull should not allow for non-boolean values. Using truthy/falsey doesn't promote INNER JOIN to an OUTER JOIN but works fine for a simple queries. Using non-boolean values is \u200bundocumented and untested. IMO we should raise an error for non-boolean values to avoid confusion and for consistency.\n", "patch": "diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py\n--- a/django/db/models/lookups.py\n+++ b/django/db/models/lookups.py\n@@ -1,5 +1,6 @@\n import itertools\n import math\n+import warnings\n from copy import copy\n \n from django.core.exceptions import EmptyResultSet\n@@ -9,6 +10,7 @@\n )\n from django.db.models.query_utils import RegisterLookupMixin\n from django.utils.datastructures import OrderedSet\n+from django.utils.deprecation import RemovedInDjango40Warning\n from django.utils.functional import cached_property\n \n \n@@ -463,6 +465,17 @@ class IsNull(BuiltinLookup):\n prepare_rhs = False\n \n def as_sql(self, compiler, connection):\n+ if not isinstance(self.rhs, bool):\n+ # When the deprecation ends, replace with:\n+ # raise ValueError(\n+ # 'The QuerySet value for an isnull lookup must be True or '\n+ # 'False.'\n+ # )\n+ warnings.warn(\n+ 'Using a non-boolean value for an isnull lookup is '\n+ 'deprecated, use True or False instead.',\n+ RemovedInDjango40Warning,\n+ )\n sql, params = compiler.compile(self.lhs)\n if self.rhs:\n return \"%s IS NULL\" % sql, params\n"} +{"instance_id": "django__django-11910", "file_changes": [{"file": "django/db/migrations/autodetector.py", "changes": {"edited_entities": ["django/db/migrations/autodetector.py:MigrationAutodetector.generate_altered_fields"], "edited_modules": ["django/db/migrations/autodetector.py:MigrationAutodetector"]}}], "repo": "django/django", "base_commit": "d232fd76a85870daf345fd8f8d617fe7802ae194", "problem_statement": "ForeignKey's to_field parameter gets the old field's name when renaming a PrimaryKey.\nDescription\n\t\nHaving these two models \nclass ModelA(models.Model):\n\tfield_wrong = models.CharField('field1', max_length=50, primary_key=True) # I'm a Primary key.\nclass ModelB(models.Model):\n\tfield_fk = models.ForeignKey(ModelA, blank=True, null=True, on_delete=models.CASCADE) \n... migrations applyed ...\nthe ModelA.field_wrong field has been renamed ... and Django recognizes the \"renaming\"\n# Primary key renamed\nclass ModelA(models.Model):\n\tfield_fixed = models.CharField('field1', max_length=50, primary_key=True) # I'm a Primary key.\nAttempts to to_field parameter. \nThe to_field points to the old_name (field_typo) and not to the new one (\"field_fixed\")\nclass Migration(migrations.Migration):\n\tdependencies = [\n\t\t('app1', '0001_initial'),\n\t]\n\toperations = [\n\t\tmigrations.RenameField(\n\t\t\tmodel_name='modela',\n\t\t\told_name='field_wrong',\n\t\t\tnew_name='field_fixed',\n\t\t),\n\t\tmigrations.AlterField(\n\t\t\tmodel_name='modelb',\n\t\t\tname='modela',\n\t\t\tfield=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='app1.ModelB', to_field='field_wrong'),\n\t\t),\n\t]\n", "patch": "diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py\n--- a/django/db/migrations/autodetector.py\n+++ b/django/db/migrations/autodetector.py\n@@ -927,6 +927,10 @@ def generate_altered_fields(self):\n if remote_field_name:\n to_field_rename_key = rename_key + (remote_field_name,)\n if to_field_rename_key in self.renamed_fields:\n+ # Repoint both model and field name because to_field\n+ # inclusion in ForeignKey.deconstruct() is based on\n+ # both.\n+ new_field.remote_field.model = old_field.remote_field.model\n new_field.remote_field.field_name = old_field.remote_field.field_name\n # Handle ForeignObjects which can have multiple from_fields/to_fields.\n from_fields = getattr(new_field, 'from_fields', None)\n"} +{"instance_id": "django__django-11964", "file_changes": [{"file": "django/db/models/enums.py", "changes": {"added_entities": ["django/db/models/enums.py:Choices.__str__"], "added_modules": ["django/db/models/enums.py:Choices"]}}], "repo": "django/django", "base_commit": "fc2b1cc926e34041953738e58fa6ad3053059b22", "problem_statement": "The value of a TextChoices/IntegerChoices field has a differing type\nDescription\n\t\nIf we create an instance of a model having a CharField or IntegerField with the keyword choices pointing to IntegerChoices or TextChoices, the value returned by the getter of the field will be of the same type as the one created by enum.Enum (enum value).\nFor example, this model:\nfrom django.db import models\nfrom django.utils.translation import gettext_lazy as _\nclass MyChoice(models.TextChoices):\n\tFIRST_CHOICE = \"first\", _(\"The first choice, it is\")\n\tSECOND_CHOICE = \"second\", _(\"The second choice, it is\")\nclass MyObject(models.Model):\n\tmy_str_value = models.CharField(max_length=10, choices=MyChoice.choices)\nThen this test:\nfrom django.test import TestCase\nfrom testing.pkg.models import MyObject, MyChoice\nclass EnumTest(TestCase):\n\tdef setUp(self) -> None:\n\t\tself.my_object = MyObject.objects.create(my_str_value=MyChoice.FIRST_CHOICE)\n\tdef test_created_object_is_str(self):\n\t\tmy_object = self.my_object\n\t\tself.assertIsInstance(my_object.my_str_value, str)\n\t\tself.assertEqual(str(my_object.my_str_value), \"first\")\n\tdef test_retrieved_object_is_str(self):\n\t\tmy_object = MyObject.objects.last()\n\t\tself.assertIsInstance(my_object.my_str_value, str)\n\t\tself.assertEqual(str(my_object.my_str_value), \"first\")\nAnd then the results:\n(django30-venv) \u279c django30 ./manage.py test\nCreating test database for alias 'default'...\nSystem check identified no issues (0 silenced).\nF.\n======================================================================\nFAIL: test_created_object_is_str (testing.tests.EnumTest)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/Users/mikailkocak/Development/django30/testing/tests.py\", line 14, in test_created_object_is_str\n\tself.assertEqual(str(my_object.my_str_value), \"first\")\nAssertionError: 'MyChoice.FIRST_CHOICE' != 'first'\n- MyChoice.FIRST_CHOICE\n+ first\n----------------------------------------------------------------------\nRan 2 tests in 0.002s\nFAILED (failures=1)\nWe notice when invoking __str__(...) we don't actually get the value property of the enum value which can lead to some unexpected issues, especially when communicating to an external API with a freshly created instance that will send MyEnum.MyValue, and the one that was retrieved would send my_value.\n", "patch": "diff --git a/django/db/models/enums.py b/django/db/models/enums.py\n--- a/django/db/models/enums.py\n+++ b/django/db/models/enums.py\n@@ -60,7 +60,13 @@ def values(cls):\n \n class Choices(enum.Enum, metaclass=ChoicesMeta):\n \"\"\"Class for creating enumerated choices.\"\"\"\n- pass\n+\n+ def __str__(self):\n+ \"\"\"\n+ Use value when cast to str, so that Choices set as model instance\n+ attributes are rendered as expected in templates and similar contexts.\n+ \"\"\"\n+ return str(self.value)\n \n \n class IntegerChoices(int, Choices):\n"} +{"instance_id": "django__django-11999", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {"edited_entities": ["django/db/models/fields/__init__.py:Field.contribute_to_class"], "edited_modules": ["django/db/models/fields/__init__.py:Field"]}}], "repo": "django/django", "base_commit": "84633905273fc916e3d17883810d9969c03f73c2", "problem_statement": "Cannot override get_FOO_display() in Django 2.2+.\nDescription\n\t\nI cannot override the get_FIELD_display function on models since version 2.2. It works in version 2.1.\nExample:\nclass FooBar(models.Model):\n\tfoo_bar = models.CharField(_(\"foo\"), choices=[(1, 'foo'), (2, 'bar')])\n\tdef __str__(self):\n\t\treturn self.get_foo_bar_display() # This returns 'foo' or 'bar' in 2.2, but 'something' in 2.1\n\tdef get_foo_bar_display(self):\n\t\treturn \"something\"\nWhat I expect is that I should be able to override this function.\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -763,8 +763,12 @@ def contribute_to_class(self, cls, name, private_only=False):\n if not getattr(cls, self.attname, None):\n setattr(cls, self.attname, self.descriptor_class(self))\n if self.choices is not None:\n- setattr(cls, 'get_%s_display' % self.name,\n- partialmethod(cls._get_FIELD_display, field=self))\n+ if not hasattr(cls, 'get_%s_display' % self.name):\n+ setattr(\n+ cls,\n+ 'get_%s_display' % self.name,\n+ partialmethod(cls._get_FIELD_display, field=self),\n+ )\n \n def get_filter_kwargs_for_object(self, obj):\n \"\"\"\n"} +{"instance_id": "django__django-12113", "file_changes": [{"file": "django/db/backends/sqlite3/creation.py", "changes": {"edited_entities": ["django/db/backends/sqlite3/creation.py:DatabaseCreation.test_db_signature"], "edited_modules": ["django/db/backends/sqlite3/creation.py:DatabaseCreation"]}}], "repo": "django/django", "base_commit": "62254c5202e80a68f4fe6572a2be46a3d953de1a", "problem_statement": "admin_views.test_multidb fails with persistent test SQLite database.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \nI've tried using persistent SQLite databases for the tests (to make use of\n--keepdb), but at least some test fails with:\nsqlite3.OperationalError: database is locked\nThis is not an issue when only using TEST[\"NAME\"] with \"default\" (which is good enough in terms of performance).\ndiff --git i/tests/test_sqlite.py w/tests/test_sqlite.py\nindex f1b65f7d01..9ce4e32e14 100644\n--- i/tests/test_sqlite.py\n+++ w/tests/test_sqlite.py\n@@ -15,9 +15,15 @@\n DATABASES = {\n\t 'default': {\n\t\t 'ENGINE': 'django.db.backends.sqlite3',\n+\t\t'TEST': {\n+\t\t\t'NAME': 'test_default.sqlite3'\n+\t\t},\n\t },\n\t 'other': {\n\t\t 'ENGINE': 'django.db.backends.sqlite3',\n+\t\t'TEST': {\n+\t\t\t'NAME': 'test_other.sqlite3'\n+\t\t},\n\t }\n }\n% tests/runtests.py admin_views.test_multidb -v 3 --keepdb --parallel 1\n\u2026\nOperations to perform:\n Synchronize unmigrated apps: admin_views, auth, contenttypes, messages, sessions, staticfiles\n Apply all migrations: admin, sites\nRunning pre-migrate handlers for application contenttypes\nRunning pre-migrate handlers for application auth\nRunning pre-migrate handlers for application sites\nRunning pre-migrate handlers for application sessions\nRunning pre-migrate handlers for application admin\nRunning pre-migrate handlers for application admin_views\nSynchronizing apps without migrations:\n Creating tables...\n\tRunning deferred SQL...\nRunning migrations:\n No migrations to apply.\nRunning post-migrate handlers for application contenttypes\nRunning post-migrate handlers for application auth\nRunning post-migrate handlers for application sites\nRunning post-migrate handlers for application sessions\nRunning post-migrate handlers for application admin\nRunning post-migrate handlers for application admin_views\nSystem check identified no issues (0 silenced).\nERROR\n======================================================================\nERROR: setUpClass (admin_views.test_multidb.MultiDatabaseTests)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"\u2026/Vcs/django/django/db/backends/utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"\u2026/Vcs/django/django/db/backends/sqlite3/base.py\", line 391, in execute\n\treturn Database.Cursor.execute(self, query, params)\nsqlite3.OperationalError: database is locked\nThe above exception was the direct cause of the following exception:\nTraceback (most recent call last):\n File \"\u2026/Vcs/django/django/test/testcases.py\", line 1137, in setUpClass\n\tcls.setUpTestData()\n File \"\u2026/Vcs/django/tests/admin_views/test_multidb.py\", line 40, in setUpTestData\n\tusername='admin', password='something', email='test@test.org',\n File \"\u2026/Vcs/django/django/contrib/auth/models.py\", line 158, in create_superuser\n\treturn self._create_user(username, email, password, **extra_fields)\n File \"\u2026/Vcs/django/django/contrib/auth/models.py\", line 141, in _create_user\n\tuser.save(using=self._db)\n File \"\u2026/Vcs/django/django/contrib/auth/base_user.py\", line 66, in save\n\tsuper().save(*args, **kwargs)\n File \"\u2026/Vcs/django/django/db/models/base.py\", line 741, in save\n\tforce_update=force_update, update_fields=update_fields)\n File \"\u2026/Vcs/django/django/db/models/base.py\", line 779, in save_base\n\tforce_update, using, update_fields,\n File \"\u2026/Vcs/django/django/db/models/base.py\", line 870, in _save_table\n\tresult = self._do_insert(cls._base_manager, using, fields, update_pk, raw)\n File \"\u2026/Vcs/django/django/db/models/base.py\", line 908, in _do_insert\n\tusing=using, raw=raw)\n File \"\u2026/Vcs/django/django/db/models/manager.py\", line 82, in manager_method\n\treturn getattr(self.get_queryset(), name)(*args, **kwargs)\n File \"\u2026/Vcs/django/django/db/models/query.py\", line 1175, in _insert\n\treturn query.get_compiler(using=using).execute_sql(return_id)\n File \"\u2026/Vcs/django/django/db/models/sql/compiler.py\", line 1321, in execute_sql\n\tcursor.execute(sql, params)\n File \"\u2026/Vcs/django/django/db/backends/utils.py\", line 67, in execute\n\treturn self._execute_with_wrappers(sql, params, many=False, executor=self._execute)\n File \"\u2026/Vcs/django/django/db/backends/utils.py\", line 76, in _execute_with_wrappers\n\treturn executor(sql, params, many, context)\n File \"\u2026/Vcs/django/django/db/backends/utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"\u2026/Vcs/django/django/db/utils.py\", line 89, in __exit__\n\traise dj_exc_value.with_traceback(traceback) from exc_value\n File \"\u2026/Vcs/django/django/db/backends/utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"\u2026/Vcs/django/django/db/backends/sqlite3/base.py\", line 391, in execute\n\treturn Database.Cursor.execute(self, query, params)\ndjango.db.utils.OperationalError: database is locked\n", "patch": "diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py\n--- a/django/db/backends/sqlite3/creation.py\n+++ b/django/db/backends/sqlite3/creation.py\n@@ -98,4 +98,6 @@ def test_db_signature(self):\n sig = [self.connection.settings_dict['NAME']]\n if self.is_in_memory_db(test_database_name):\n sig.append(self.connection.alias)\n+ else:\n+ sig.append(test_database_name)\n return tuple(sig)\n"} +{"instance_id": "django__django-12125", "file_changes": [{"file": "django/db/migrations/serializer.py", "changes": {"edited_entities": ["django/db/migrations/serializer.py:TypeSerializer.serialize"], "edited_modules": ["django/db/migrations/serializer.py:TypeSerializer"]}}], "repo": "django/django", "base_commit": "89d41cba392b759732ba9f1db4ff29ed47da6a56", "problem_statement": "makemigrations produces incorrect path for inner classes\nDescription\n\t\nWhen you define a subclass from django.db.models.Field as an inner class of some other class, and use this field inside a django.db.models.Model class, then when you run manage.py makemigrations, a migrations file is created which refers to the inner class as if it were a top-level class of the module it is in.\nTo reproduce, create the following as your model:\nclass Outer(object):\n\tclass Inner(models.CharField):\n\t\tpass\nclass A(models.Model):\n\tfield = Outer.Inner(max_length=20)\nAfter running manage.py makemigrations, the generated migrations file contains the following:\nmigrations.CreateModel(\n\tname='A',\n\tfields=[\n\t\t('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n\t\t('field', test1.models.Inner(max_length=20)),\n\t],\n),\nNote the test1.models.Inner, which should have been test1.models.Outer.Inner.\nThe real life case involved an EnumField from django-enumfields, defined as an inner class of a Django Model class, similar to this:\nimport enum\nfrom enumfields import Enum, EnumField\nclass Thing(models.Model):\n\t@enum.unique\n\tclass State(Enum):\n\t\ton = 'on'\n\t\toff = 'off'\n\tstate = EnumField(enum=State)\nThis results in the following migrations code:\nmigrations.CreateModel(\n\tname='Thing',\n\tfields=[\n\t\t('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n\t\t('state', enumfields.fields.EnumField(enum=test1.models.State, max_length=10)),\n\t],\n),\nThis refers to test1.models.State, instead of to test1.models.Thing.State.\n", "patch": "diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py\n--- a/django/db/migrations/serializer.py\n+++ b/django/db/migrations/serializer.py\n@@ -269,7 +269,7 @@ def serialize(self):\n if module == builtins.__name__:\n return self.value.__name__, set()\n else:\n- return \"%s.%s\" % (module, self.value.__name__), {\"import %s\" % module}\n+ return \"%s.%s\" % (module, self.value.__qualname__), {\"import %s\" % module}\n \n \n class UUIDSerializer(BaseSerializer):\n"} +{"instance_id": "django__django-12184", "file_changes": [{"file": "django/urls/resolvers.py", "changes": {"edited_entities": ["django/urls/resolvers.py:RegexPattern.match"], "edited_modules": ["django/urls/resolvers.py:RegexPattern"]}}], "repo": "django/django", "base_commit": "5d674eac871a306405b0fbbaeb17bbeba9c68bf3", "problem_statement": "Optional URL params crash some view functions.\nDescription\n\t\nMy use case, running fine with Django until 2.2:\nURLConf:\nurlpatterns += [\n\t...\n\tre_path(r'^module/(?P(html|json|xml))?/?$', views.modules, name='modules'),\n]\nView:\ndef modules(request, format='html'):\n\t...\n\treturn render(...)\nWith Django 3.0, this is now producing an error:\nTraceback (most recent call last):\n File \"/l10n/venv/lib/python3.6/site-packages/django/core/handlers/exception.py\", line 34, in inner\n\tresponse = get_response(request)\n File \"/l10n/venv/lib/python3.6/site-packages/django/core/handlers/base.py\", line 115, in _get_response\n\tresponse = self.process_exception_by_middleware(e, request)\n File \"/l10n/venv/lib/python3.6/site-packages/django/core/handlers/base.py\", line 113, in _get_response\n\tresponse = wrapped_callback(request, *callback_args, **callback_kwargs)\nException Type: TypeError at /module/\nException Value: modules() takes from 1 to 2 positional arguments but 3 were given\n", "patch": "diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py\n--- a/django/urls/resolvers.py\n+++ b/django/urls/resolvers.py\n@@ -158,8 +158,9 @@ def match(self, path):\n # If there are any named groups, use those as kwargs, ignoring\n # non-named groups. Otherwise, pass all non-named arguments as\n # positional arguments.\n- kwargs = {k: v for k, v in match.groupdict().items() if v is not None}\n+ kwargs = match.groupdict()\n args = () if kwargs else match.groups()\n+ kwargs = {k: v for k, v in kwargs.items() if v is not None}\n return path[match.end():], args, kwargs\n return None\n \n"} +{"instance_id": "django__django-12284", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {"edited_entities": ["django/db/models/fields/__init__.py:Field.contribute_to_class"], "edited_modules": ["django/db/models/fields/__init__.py:Field"]}}], "repo": "django/django", "base_commit": "c5e373d48cbdd923575956fed477b63d66d9603f", "problem_statement": "Model.get_FOO_display() does not work correctly with inherited choices.\nDescription\n\t \n\t\t(last modified by Mariusz Felisiak)\n\t \nGiven a base model with choices A containing 3 tuples\nChild Model inherits the base model overrides the choices A and adds 2 more tuples\nget_foo_display does not work correctly for the new tuples added\nExample:\nclass A(models.Model):\n foo_choice = [(\"A\",\"output1\"),(\"B\",\"output2\")]\n field_foo = models.CharField(max_length=254,choices=foo_choice)\n class Meta:\n\t abstract = True\nclass B(A):\n foo_choice = [(\"A\",\"output1\"),(\"B\",\"output2\"),(\"C\",\"output3\")]\n field_foo = models.CharField(max_length=254,choices=foo_choice)\nUpon invoking get_field_foo_display() on instance of B , \nFor value \"A\" and \"B\" the output works correctly i.e. returns \"output1\" / \"output2\"\nbut for value \"C\" the method returns \"C\" and not \"output3\" which is the expected behaviour\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -764,7 +764,11 @@ def contribute_to_class(self, cls, name, private_only=False):\n if not getattr(cls, self.attname, None):\n setattr(cls, self.attname, self.descriptor_class(self))\n if self.choices is not None:\n- if not hasattr(cls, 'get_%s_display' % self.name):\n+ # Don't override a get_FOO_display() method defined explicitly on\n+ # this class, but don't check methods derived from inheritance, to\n+ # allow overriding inherited choices. For more complex inheritance\n+ # structures users should override contribute_to_class().\n+ if 'get_%s_display' % self.name not in cls.__dict__:\n setattr(\n cls,\n 'get_%s_display' % self.name,\n"} +{"instance_id": "django__django-12286", "file_changes": [{"file": "django/core/checks/translation.py", "changes": {"edited_entities": ["django/core/checks/translation.py:check_language_settings_consistent"], "edited_modules": ["django/core/checks/translation.py:check_language_settings_consistent"]}}], "repo": "django/django", "base_commit": "979f61abd322507aafced9627702362e541ec34e", "problem_statement": "translation.E004 shouldn't be raised on sublanguages when a base language is available.\nDescription\n\t\nAccording to Django documentation:\nIf a base language is available but the sublanguage specified is not, Django uses the base language. For example, if a user specifies de-at (Austrian German) but Django only has de available, Django uses de.\nHowever, when using Django 3.0.2, if my settings.py has\nLANGUAGE_CODE = \"de-at\"\nI get this error message:\nSystemCheckError: System check identified some issues:\nERRORS:\n?: (translation.E004) You have provided a value for the LANGUAGE_CODE setting that is not in the LANGUAGES setting.\nIf using\nLANGUAGE_CODE = \"es-ar\"\nDjango works fine (es-ar is one of the translations provided out of the box).\n", "patch": "diff --git a/django/core/checks/translation.py b/django/core/checks/translation.py\n--- a/django/core/checks/translation.py\n+++ b/django/core/checks/translation.py\n@@ -1,4 +1,5 @@\n from django.conf import settings\n+from django.utils.translation import get_supported_language_variant\n from django.utils.translation.trans_real import language_code_re\n \n from . import Error, Tags, register\n@@ -55,7 +56,9 @@ def check_setting_languages_bidi(app_configs, **kwargs):\n @register(Tags.translation)\n def check_language_settings_consistent(app_configs, **kwargs):\n \"\"\"Error if language settings are not consistent with each other.\"\"\"\n- available_tags = {i for i, _ in settings.LANGUAGES} | {'en-us'}\n- if settings.LANGUAGE_CODE not in available_tags:\n+ try:\n+ get_supported_language_variant(settings.LANGUAGE_CODE)\n+ except LookupError:\n return [E004]\n- return []\n+ else:\n+ return []\n"} +{"instance_id": "django__django-12308", "file_changes": [{"file": "django/contrib/admin/utils.py", "changes": {"edited_entities": ["django/contrib/admin/utils.py:display_for_field"], "edited_modules": ["django/contrib/admin/utils.py:display_for_field"]}}], "repo": "django/django", "base_commit": "2e0f04507b17362239ba49830d26fec504d46978", "problem_statement": "JSONField are not properly displayed in admin when they are readonly.\nDescription\n\t\nJSONField values are displayed as dict when readonly in the admin.\nFor example, {\"foo\": \"bar\"} would be displayed as {'foo': 'bar'}, which is not valid JSON.\nI believe the fix would be to add a special case in django.contrib.admin.utils.display_for_field to call the prepare_value of the JSONField (not calling json.dumps directly to take care of the InvalidJSONInput case).\n", "patch": "diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py\n--- a/django/contrib/admin/utils.py\n+++ b/django/contrib/admin/utils.py\n@@ -398,6 +398,11 @@ def display_for_field(value, field, empty_value_display):\n return formats.number_format(value)\n elif isinstance(field, models.FileField) and value:\n return format_html('{}', value.url, value)\n+ elif isinstance(field, models.JSONField) and value:\n+ try:\n+ return field.get_prep_value(value)\n+ except TypeError:\n+ return display_for_value(value, empty_value_display)\n else:\n return display_for_value(value, empty_value_display)\n \n"} +{"instance_id": "django__django-12453", "file_changes": [{"file": "django/db/backends/base/creation.py", "changes": {"edited_entities": ["django/db/backends/base/creation.py:BaseDatabaseCreation.deserialize_db_from_string"], "edited_modules": ["django/db/backends/base/creation.py:BaseDatabaseCreation"]}}], "repo": "django/django", "base_commit": "b330b918e979ea39a21d47b61172d112caf432c3", "problem_statement": "`TransactionTestCase.serialized_rollback` fails to restore objects due to ordering constraints\nDescription\n\t\nI hit this problem in a fairly complex projet and haven't had the time to write a minimal reproduction case. I think it can be understood just by inspecting the code so I'm going to describe it while I have it in mind.\nSetting serialized_rollback = True on a TransactionTestCase triggers \u200brollback emulation. In practice, for each database:\nBaseDatabaseCreation.create_test_db calls connection._test_serialized_contents = connection.creation.serialize_db_to_string()\nTransactionTestCase._fixture_setup calls connection.creation.deserialize_db_from_string(connection._test_serialized_contents)\n(The actual code isn't written that way; it's equivalent but the symmetry is less visible.)\nserialize_db_to_string orders models with serializers.sort_dependencies and serializes them. The sorting algorithm only deals with natural keys. It doesn't do anything to order models referenced by foreign keys before models containing said foreign keys. That wouldn't be possible in general because circular foreign keys are allowed.\ndeserialize_db_from_string deserializes and saves models without wrapping in a transaction. This can result in integrity errors if an instance containing a foreign key is saved before the instance it references. I'm suggesting to fix it as follows:\ndiff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py\nindex bca8376..7bed2be 100644\n--- a/django/db/backends/base/creation.py\n+++ b/django/db/backends/base/creation.py\n@@ -4,7 +4,7 @@ import time\n from django.apps import apps\n from django.conf import settings\n from django.core import serializers\n-from django.db import router\n+from django.db import router, transaction\n from django.utils.six import StringIO\n from django.utils.six.moves import input\n \n@@ -128,8 +128,9 @@ class BaseDatabaseCreation(object):\n\t\t the serialize_db_to_string method.\n\t\t \"\"\"\n\t\t data = StringIO(data)\n-\t\tfor obj in serializers.deserialize(\"json\", data, using=self.connection.alias):\n-\t\t\tobj.save()\n+\t\twith transaction.atomic(using=self.connection.alias):\n+\t\t\tfor obj in serializers.deserialize(\"json\", data, using=self.connection.alias):\n+\t\t\t\tobj.save()\n \n\t def _get_database_display_str(self, verbosity, database_name):\n\t\t \"\"\"\nNote that loaddata doesn't have this problem because it wraps everything in a transaction:\n\tdef handle(self, *fixture_labels, **options):\n\t\t# ...\n\t\twith transaction.atomic(using=self.using):\n\t\t\tself.loaddata(fixture_labels)\n\t\t# ...\nThis suggest that the transaction was just forgotten in the implementation of deserialize_db_from_string.\nIt should be possible to write a deterministic test for this bug because the order in which serialize_db_to_string serializes models depends on the app registry, and the app registry uses OrderedDict to store apps and models in a deterministic order.\n", "patch": "diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py\n--- a/django/db/backends/base/creation.py\n+++ b/django/db/backends/base/creation.py\n@@ -6,6 +6,7 @@\n from django.conf import settings\n from django.core import serializers\n from django.db import router\n+from django.db.transaction import atomic\n \n # The prefix to put on the default database name when creating\n # the test database.\n@@ -126,8 +127,16 @@ def deserialize_db_from_string(self, data):\n the serialize_db_to_string() method.\n \"\"\"\n data = StringIO(data)\n- for obj in serializers.deserialize(\"json\", data, using=self.connection.alias):\n- obj.save()\n+ # Load data in a transaction to handle forward references and cycles.\n+ with atomic(using=self.connection.alias):\n+ # Disable constraint checks, because some databases (MySQL) doesn't\n+ # support deferred checks.\n+ with self.connection.constraint_checks_disabled():\n+ for obj in serializers.deserialize('json', data, using=self.connection.alias):\n+ obj.save()\n+ # Manually check for any invalid keys that might have been added,\n+ # because constraint checks were disabled.\n+ self.connection.check_constraints()\n \n def _get_database_display_str(self, verbosity, database_name):\n \"\"\"\n"} +{"instance_id": "django__django-12470", "file_changes": [{"file": "django/db/models/sql/compiler.py", "changes": {"edited_entities": ["django/db/models/sql/compiler.py:SQLCompiler.find_ordering_name"], "edited_modules": ["django/db/models/sql/compiler.py:SQLCompiler"]}}], "repo": "django/django", "base_commit": "142ab6846ac09d6d401e26fc8b6b988a583ac0f5", "problem_statement": "Inherited model doesn't correctly order by \"-pk\" when specified on Parent.Meta.ordering\nDescription\n\t\nGiven the following model definition:\nfrom django.db import models\nclass Parent(models.Model):\n\tclass Meta:\n\t\tordering = [\"-pk\"]\nclass Child(Parent):\n\tpass\nQuerying the Child class results in the following:\n>>> print(Child.objects.all().query)\nSELECT \"myapp_parent\".\"id\", \"myapp_child\".\"parent_ptr_id\" FROM \"myapp_child\" INNER JOIN \"myapp_parent\" ON (\"myapp_child\".\"parent_ptr_id\" = \"myapp_parent\".\"id\") ORDER BY \"myapp_parent\".\"id\" ASC\nThe query is ordered ASC but I expect the order to be DESC.\n", "patch": "diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py\n--- a/django/db/models/sql/compiler.py\n+++ b/django/db/models/sql/compiler.py\n@@ -709,9 +709,9 @@ def find_ordering_name(self, name, opts, alias=None, default_order='ASC',\n field, targets, alias, joins, path, opts, transform_function = self._setup_joins(pieces, opts, alias)\n \n # If we get to this point and the field is a relation to another model,\n- # append the default ordering for that model unless the attribute name\n- # of the field is specified.\n- if field.is_relation and opts.ordering and getattr(field, 'attname', None) != name:\n+ # append the default ordering for that model unless it is the pk\n+ # shortcut or the attribute name of the field that is specified.\n+ if field.is_relation and opts.ordering and getattr(field, 'attname', None) != name and name != 'pk':\n # Firstly, avoid infinite loops.\n already_seen = already_seen or set()\n join_tuple = tuple(getattr(self.query.alias_map[j], 'join_cols', None) for j in joins)\n"} +{"instance_id": "django__django-12497", "file_changes": [{"file": "django/db/models/fields/related.py", "changes": {"edited_entities": ["django/db/models/fields/related.py:ManyToManyField._check_relationship_model"], "edited_modules": ["django/db/models/fields/related.py:ManyToManyField"]}}], "repo": "django/django", "base_commit": "a4881f5e5d7ee38b7e83301331a0b4962845ef8a", "problem_statement": "Wrong hint about recursive relationship.\nDescription\n\t \n\t\t(last modified by Matheus Cunha Motta)\n\t \nWhen there's more than 2 ForeignKeys in an intermediary model of a m2m field and no through_fields have been set, Django will show an error with the following hint:\nhint=(\n\t'If you want to create a recursive relationship, '\n\t'use ForeignKey(\"%s\", symmetrical=False, through=\"%s\").'\nBut 'symmetrical' and 'through' are m2m keyword arguments, not ForeignKey.\nThis was probably a small mistake where the developer thought ManyToManyField but typed ForeignKey instead. And the symmetrical=False is an outdated requirement to recursive relationships with intermediary model to self, not required since 3.0. I'll provide a PR with a proposed correction shortly after.\nEdit: fixed description.\n", "patch": "diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py\n--- a/django/db/models/fields/related.py\n+++ b/django/db/models/fields/related.py\n@@ -1309,7 +1309,7 @@ def _check_relationship_model(self, from_model=None, **kwargs):\n \"through_fields keyword argument.\") % (self, from_model_name),\n hint=(\n 'If you want to create a recursive relationship, '\n- 'use ForeignKey(\"%s\", symmetrical=False, through=\"%s\").'\n+ 'use ManyToManyField(\"%s\", through=\"%s\").'\n ) % (\n RECURSIVE_RELATIONSHIP_CONSTANT,\n relationship_model_name,\n@@ -1329,7 +1329,7 @@ def _check_relationship_model(self, from_model=None, **kwargs):\n \"through_fields keyword argument.\" % (self, to_model_name),\n hint=(\n 'If you want to create a recursive relationship, '\n- 'use ForeignKey(\"%s\", symmetrical=False, through=\"%s\").'\n+ 'use ManyToManyField(\"%s\", through=\"%s\").'\n ) % (\n RECURSIVE_RELATIONSHIP_CONSTANT,\n relationship_model_name,\n"} +{"instance_id": "django__django-12589", "file_changes": [{"file": "django/db/models/sql/query.py", "changes": {"edited_entities": ["django/db/models/sql/query.py:Query.set_group_by"], "edited_modules": ["django/db/models/sql/query.py:Query"]}}], "repo": "django/django", "base_commit": "895f28f9cbed817c00ab68770433170d83132d90", "problem_statement": "Django 3.0: \"GROUP BY\" clauses error with tricky field annotation\nDescription\n\t\nLet's pretend that we have next model structure with next model's relations:\nclass A(models.Model):\n\tbs = models.ManyToManyField('B',\n\t\t\t\t\t\t\t\trelated_name=\"a\",\n\t\t\t\t\t\t\t\tthrough=\"AB\")\nclass B(models.Model):\n\tpass\nclass AB(models.Model):\n\ta = models.ForeignKey(A, on_delete=models.CASCADE, related_name=\"ab_a\")\n\tb = models.ForeignKey(B, on_delete=models.CASCADE, related_name=\"ab_b\")\n\tstatus = models.IntegerField()\nclass C(models.Model):\n\ta = models.ForeignKey(\n\t\tA,\n\t\tnull=True,\n\t\tblank=True,\n\t\ton_delete=models.SET_NULL,\n\t\trelated_name=\"c\",\n\t\tverbose_name=_(\"a\")\n\t)\n\tstatus = models.IntegerField()\nLet's try to evaluate next query\nab_query = AB.objects.filter(a=OuterRef(\"pk\"), b=1)\nfilter_conditions = Q(pk=1) | Q(ab_a__b=1)\nquery = A.objects.\\\n\tfilter(filter_conditions).\\\n\tannotate(\n\t\tstatus=Subquery(ab_query.values(\"status\")),\n\t\tc_count=Count(\"c\"),\n)\nanswer = query.values(\"status\").annotate(total_count=Count(\"status\"))\nprint(answer.query)\nprint(answer)\nOn Django 3.0.4 we have an error\ndjango.db.utils.ProgrammingError: column reference \"status\" is ambiguous\nand query is next:\nSELECT (SELECT U0.\"status\" FROM \"test_app_ab\" U0 WHERE (U0.\"a_id\" = \"test_app_a\".\"id\" AND U0.\"b_id\" = 1)) AS \"status\", COUNT((SELECT U0.\"status\" FROM \"test_app_ab\" U0 WHERE (U0.\"a_id\" = \"test_app_a\".\"id\" AND U0.\"b_id\" = 1))) AS \"total_count\" FROM \"test_app_a\" LEFT OUTER JOIN \"test_app_ab\" ON (\"test_app_a\".\"id\" = \"test_app_ab\".\"a_id\") LEFT OUTER JOIN \"test_app_c\" ON (\"test_app_a\".\"id\" = \"test_app_c\".\"a_id\") WHERE (\"test_app_a\".\"id\" = 1 OR \"test_app_ab\".\"b_id\" = 1) GROUP BY \"status\"\nHowever, Django 2.2.11 processed this query properly with the next query:\nSELECT (SELECT U0.\"status\" FROM \"test_app_ab\" U0 WHERE (U0.\"a_id\" = (\"test_app_a\".\"id\") AND U0.\"b_id\" = 1)) AS \"status\", COUNT((SELECT U0.\"status\" FROM \"test_app_ab\" U0 WHERE (U0.\"a_id\" = (\"test_app_a\".\"id\") AND U0.\"b_id\" = 1))) AS \"total_count\" FROM \"test_app_a\" LEFT OUTER JOIN \"test_app_ab\" ON (\"test_app_a\".\"id\" = \"test_app_ab\".\"a_id\") LEFT OUTER JOIN \"test_app_c\" ON (\"test_app_a\".\"id\" = \"test_app_c\".\"a_id\") WHERE (\"test_app_a\".\"id\" = 1 OR \"test_app_ab\".\"b_id\" = 1) GROUP BY (SELECT U0.\"status\" FROM \"test_app_ab\" U0 WHERE (U0.\"a_id\" = (\"test_app_a\".\"id\") AND U0.\"b_id\" = 1))\nso, the difference in \"GROUP BY\" clauses\n(as DB provider uses \"django.db.backends.postgresql\", postgresql 11)\n", "patch": "diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py\n--- a/django/db/models/sql/query.py\n+++ b/django/db/models/sql/query.py\n@@ -1927,6 +1927,19 @@ def set_group_by(self, allow_aliases=True):\n primary key, and the query would be equivalent, the optimization\n will be made automatically.\n \"\"\"\n+ # Column names from JOINs to check collisions with aliases.\n+ if allow_aliases:\n+ column_names = set()\n+ seen_models = set()\n+ for join in list(self.alias_map.values())[1:]: # Skip base table.\n+ model = join.join_field.related_model\n+ if model not in seen_models:\n+ column_names.update({\n+ field.column\n+ for field in model._meta.local_concrete_fields\n+ })\n+ seen_models.add(model)\n+\n group_by = list(self.select)\n if self.annotation_select:\n for alias, annotation in self.annotation_select.items():\n@@ -1940,7 +1953,7 @@ def set_group_by(self, allow_aliases=True):\n warnings.warn(msg, category=RemovedInDjango40Warning)\n group_by_cols = annotation.get_group_by_cols()\n else:\n- if not allow_aliases:\n+ if not allow_aliases or alias in column_names:\n alias = None\n group_by_cols = annotation.get_group_by_cols(alias=alias)\n group_by.extend(group_by_cols)\n"} +{"instance_id": "django__django-12700", "file_changes": [{"file": "django/views/debug.py", "changes": {"edited_entities": ["django/views/debug.py:SafeExceptionReporterFilter.cleanse_setting"], "edited_modules": ["django/views/debug.py:SafeExceptionReporterFilter"]}}], "repo": "django/django", "base_commit": "d51c50d836c5cf8db5566da17963f871be554615", "problem_statement": "Settings are cleaned insufficiently.\nDescription\n\t\nPosting publicly after checking with the rest of the security team.\nI just ran into a case where django.views.debug.SafeExceptionReporterFilter.get_safe_settings() would return several un-cleansed values. Looking at cleanse_setting() I realized that we \u200bonly take care of `dict`s but don't take other types of iterables into account but \u200breturn them as-is.\nExample:\nIn my settings.py I have this:\nMY_SETTING = {\n\t\"foo\": \"value\",\n\t\"secret\": \"value\",\n\t\"token\": \"value\",\n\t\"something\": [\n\t\t{\"foo\": \"value\"},\n\t\t{\"secret\": \"value\"},\n\t\t{\"token\": \"value\"},\n\t],\n\t\"else\": [\n\t\t[\n\t\t\t{\"foo\": \"value\"},\n\t\t\t{\"secret\": \"value\"},\n\t\t\t{\"token\": \"value\"},\n\t\t],\n\t\t[\n\t\t\t{\"foo\": \"value\"},\n\t\t\t{\"secret\": \"value\"},\n\t\t\t{\"token\": \"value\"},\n\t\t],\n\t]\n}\nOn Django 3.0 and below:\n>>> import pprint\n>>> from django.views.debug import get_safe_settings\n>>> pprint.pprint(get_safe_settings()[\"MY_SETTING\"])\n{'else': [[{'foo': 'value'}, {'secret': 'value'}, {'token': 'value'}],\n\t\t [{'foo': 'value'}, {'secret': 'value'}, {'token': 'value'}]],\n 'foo': 'value',\n 'secret': '********************',\n 'something': [{'foo': 'value'}, {'secret': 'value'}, {'token': 'value'}],\n 'token': '********************'}\nOn Django 3.1 and up:\n>>> from django.views.debug import SafeExceptionReporterFilter\n>>> import pprint\n>>> pprint.pprint(SafeExceptionReporterFilter().get_safe_settings()[\"MY_SETTING\"])\n{'else': [[{'foo': 'value'}, {'secret': 'value'}, {'token': 'value'}],\n\t\t [{'foo': 'value'}, {'secret': 'value'}, {'token': 'value'}]],\n 'foo': 'value',\n 'secret': '********************',\n 'something': [{'foo': 'value'}, {'secret': 'value'}, {'token': 'value'}],\n 'token': '********************'}\n", "patch": "diff --git a/django/views/debug.py b/django/views/debug.py\n--- a/django/views/debug.py\n+++ b/django/views/debug.py\n@@ -90,6 +90,10 @@ def cleanse_setting(self, key, value):\n cleansed = self.cleansed_substitute\n elif isinstance(value, dict):\n cleansed = {k: self.cleanse_setting(k, v) for k, v in value.items()}\n+ elif isinstance(value, list):\n+ cleansed = [self.cleanse_setting('', v) for v in value]\n+ elif isinstance(value, tuple):\n+ cleansed = tuple([self.cleanse_setting('', v) for v in value])\n else:\n cleansed = value\n except TypeError:\n"} +{"instance_id": "django__django-12708", "file_changes": [{"file": "django/db/backends/base/schema.py", "changes": {"edited_entities": ["django/db/backends/base/schema.py:BaseDatabaseSchemaEditor.alter_index_together"], "edited_modules": ["django/db/backends/base/schema.py:BaseDatabaseSchemaEditor"]}}], "repo": "django/django", "base_commit": "447980e72ac01da1594dd3373a03ba40b7ee6f80", "problem_statement": "Migration crashes deleting an index_together if there is a unique_together on the same fields\nDescription\n\t\nHappens with Django 1.11.10\nSteps to reproduce:\n1) Create models with 2 fields, add 2 same fields to unique_together and to index_together\n2) Delete index_together -> Fail\nIt will fail at django/db/backends/base/schema.py, line 378, in _delete_composed_index(), ValueError: Found wrong number (2) of constraints for as this one will find two constraints, the _uniq and the _idx one. No way to get out of this...\nThe worst in my case is that happened as I wanted to refactor my code to use the \"new\" (Dj 1.11) Options.indexes feature. I am actually not deleting the index, just the way it is declared in my code.\nI think there are 2 different points here:\n1) The deletion of index_together should be possible alone or made coherent (migrations side?) with unique_together\n2) Moving the declaration of an index should not result in an index re-creation\n", "patch": "diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py\n--- a/django/db/backends/base/schema.py\n+++ b/django/db/backends/base/schema.py\n@@ -393,7 +393,12 @@ def alter_index_together(self, model, old_index_together, new_index_together):\n news = {tuple(fields) for fields in new_index_together}\n # Deleted indexes\n for fields in olds.difference(news):\n- self._delete_composed_index(model, fields, {'index': True}, self.sql_delete_index)\n+ self._delete_composed_index(\n+ model,\n+ fields,\n+ {'index': True, 'unique': False},\n+ self.sql_delete_index,\n+ )\n # Created indexes\n for field_names in news.difference(olds):\n fields = [model._meta.get_field(field) for field in field_names]\n"} +{"instance_id": "django__django-12747", "file_changes": [{"file": "django/db/models/deletion.py", "changes": {"edited_entities": ["django/db/models/deletion.py:Collector.delete"], "edited_modules": ["django/db/models/deletion.py:Collector"]}}], "repo": "django/django", "base_commit": "c86201b6ed4f8256b0a0520c08aa674f623d4127", "problem_statement": "QuerySet.Delete - inconsistent result when zero objects deleted\nDescription\n\t\nThe result format of the QuerySet.Delete method is a tuple: (X, Y) \nX - is the total amount of deleted objects (including foreign key deleted objects)\nY - is a dictionary specifying counters of deleted objects for each specific model (the key is the _meta.label of the model and the value is counter of deleted objects of this model).\nExample: : (2, {'my_app.FileAccess': 1, 'my_app.File': 1})\nWhen there are zero objects to delete in total - the result is inconsistent:\nFor models with foreign keys - the result will be: : (0, {})\nFor \"simple\" models without foreign key - the result will be: : (0, {'my_app.BlockLibrary': 0})\nI would expect there will be no difference between the two cases: Either both will have the empty dictionary OR both will have dictionary with model-label keys and zero value.\n", "patch": "diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py\n--- a/django/db/models/deletion.py\n+++ b/django/db/models/deletion.py\n@@ -408,7 +408,8 @@ def delete(self):\n # fast deletes\n for qs in self.fast_deletes:\n count = qs._raw_delete(using=self.using)\n- deleted_counter[qs.model._meta.label] += count\n+ if count:\n+ deleted_counter[qs.model._meta.label] += count\n \n # update fields\n for model, instances_for_fieldvalues in self.field_updates.items():\n@@ -426,7 +427,8 @@ def delete(self):\n query = sql.DeleteQuery(model)\n pk_list = [obj.pk for obj in instances]\n count = query.delete_batch(pk_list, self.using)\n- deleted_counter[model._meta.label] += count\n+ if count:\n+ deleted_counter[model._meta.label] += count\n \n if not model._meta.auto_created:\n for obj in instances:\n"} +{"instance_id": "django__django-12856", "file_changes": [{"file": "django/db/models/base.py", "changes": {"edited_entities": ["django/db/models/base.py:Model._check_constraints"], "edited_modules": ["django/db/models/base.py:Model"]}}], "repo": "django/django", "base_commit": "8328811f048fed0dd22573224def8c65410c9f2e", "problem_statement": "Add check for fields of UniqueConstraints.\nDescription\n\t \n\t\t(last modified by Marnanel Thurman)\n\t \nWhen a model gains a UniqueConstraint, makemigrations doesn't check that the fields named therein actually exist.\nThis is in contrast to the older unique_together syntax, which raises models.E012 if the fields don't exist.\nIn the attached demonstration, you'll need to uncomment \"with_unique_together\" in settings.py in order to show that unique_together raises E012.\n", "patch": "diff --git a/django/db/models/base.py b/django/db/models/base.py\n--- a/django/db/models/base.py\n+++ b/django/db/models/base.py\n@@ -1926,6 +1926,12 @@ def _check_constraints(cls, databases):\n id='models.W038',\n )\n )\n+ fields = (\n+ field\n+ for constraint in cls._meta.constraints if isinstance(constraint, UniqueConstraint)\n+ for field in constraint.fields\n+ )\n+ errors.extend(cls._check_local_fields(fields, 'constraints'))\n return errors\n \n \n"} +{"instance_id": "django__django-12908", "file_changes": [{"file": "django/db/models/query.py", "changes": {"edited_entities": ["django/db/models/query.py:QuerySet.distinct"], "edited_modules": ["django/db/models/query.py:QuerySet"]}}], "repo": "django/django", "base_commit": "49ae7ce50a874f8a04cd910882fb9571ff3a0d7a", "problem_statement": "Union queryset should raise on distinct().\nDescription\n\t \n\t\t(last modified by Sielc Technologies)\n\t \nAfter using\n.annotate() on 2 different querysets\nand then .union()\n.distinct() will not affect the queryset\n\tdef setUp(self) -> None:\n\t\tuser = self.get_or_create_admin_user()\n\t\tSample.h.create(user, name=\"Sam1\")\n\t\tSample.h.create(user, name=\"Sam2 acid\")\n\t\tSample.h.create(user, name=\"Sam3\")\n\t\tSample.h.create(user, name=\"Sam4 acid\")\n\t\tSample.h.create(user, name=\"Dub\")\n\t\tSample.h.create(user, name=\"Dub\")\n\t\tSample.h.create(user, name=\"Dub\")\n\t\tself.user = user\n\tdef test_union_annotated_diff_distinct(self):\n\t\tqs = Sample.objects.filter(user=self.user)\n\t\tqs1 = qs.filter(name='Dub').annotate(rank=Value(0, IntegerField()))\n\t\tqs2 = qs.filter(name='Sam1').annotate(rank=Value(1, IntegerField()))\n\t\tqs = qs1.union(qs2)\n\t\tqs = qs.order_by('name').distinct('name') # THIS DISTINCT DOESN'T WORK\n\t\tself.assertEqual(qs.count(), 2)\nexpected to get wrapped union\n\tSELECT DISTINCT ON (siebox_sample.name) * FROM (SELECT ... UNION SELECT ...) AS siebox_sample\n", "patch": "diff --git a/django/db/models/query.py b/django/db/models/query.py\n--- a/django/db/models/query.py\n+++ b/django/db/models/query.py\n@@ -1138,6 +1138,7 @@ def distinct(self, *field_names):\n \"\"\"\n Return a new QuerySet instance that will select only distinct results.\n \"\"\"\n+ self._not_support_combined_queries('distinct')\n assert not self.query.is_sliced, \\\n \"Cannot create distinct fields once a slice has been taken.\"\n obj = self._chain()\n"} +{"instance_id": "django__django-12915", "file_changes": [{"file": "django/contrib/staticfiles/handlers.py", "changes": {"added_entities": ["django/contrib/staticfiles/handlers.py:StaticFilesHandlerMixin.get_response_async"], "added_modules": ["django/contrib/staticfiles/handlers.py:StaticFilesHandlerMixin"]}}], "repo": "django/django", "base_commit": "4652f1f0aa459a7b980441d629648707c32e36bf", "problem_statement": "Add get_response_async for ASGIStaticFilesHandler\nDescription\n\t\nIt looks like the StaticFilesHandlerMixin is missing the the async response function.\nWithout this, when trying to use the ASGIStaticFilesHandler, this is the traceback:\nException inside application: 'NoneType' object is not callable\nTraceback (most recent call last):\n File \".../lib/python3.7/site-packages/daphne/cli.py\", line 30, in asgi\n\tawait self.app(scope, receive, send)\n File \".../src/django/django/contrib/staticfiles/handlers.py\", line 86, in __call__\n\treturn await super().__call__(scope, receive, send)\n File \".../src/django/django/core/handlers/asgi.py\", line 161, in __call__\n\tresponse = await self.get_response_async(request)\n File \".../src/django/django/core/handlers/base.py\", line 148, in get_response_async\n\tresponse = await self._middleware_chain(request)\nTypeError: 'NoneType' object is not callable\n", "patch": "diff --git a/django/contrib/staticfiles/handlers.py b/django/contrib/staticfiles/handlers.py\n--- a/django/contrib/staticfiles/handlers.py\n+++ b/django/contrib/staticfiles/handlers.py\n@@ -1,6 +1,8 @@\n from urllib.parse import urlparse\n from urllib.request import url2pathname\n \n+from asgiref.sync import sync_to_async\n+\n from django.conf import settings\n from django.contrib.staticfiles import utils\n from django.contrib.staticfiles.views import serve\n@@ -52,6 +54,12 @@ def get_response(self, request):\n except Http404 as e:\n return response_for_exception(request, e)\n \n+ async def get_response_async(self, request):\n+ try:\n+ return await sync_to_async(self.serve)(request)\n+ except Http404 as e:\n+ return await sync_to_async(response_for_exception)(request, e)\n+\n \n class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler):\n \"\"\"\n"} +{"instance_id": "django__django-12983", "file_changes": [{"file": "django/utils/text.py", "changes": {"edited_entities": ["django/utils/text.py:slugify"], "edited_modules": ["django/utils/text.py:slugify"]}}], "repo": "django/django", "base_commit": "3bc4240d979812bd11365ede04c028ea13fdc8c6", "problem_statement": "Make django.utils.text.slugify() strip dashes and underscores\nDescription\n\t \n\t\t(last modified by Elinaldo do Nascimento Monteiro)\n\t \nBug generation slug\nExample:\nfrom django.utils import text\ntext.slugify(\"___This is a test ---\")\noutput: ___this-is-a-test-\nImprovement after correction\nfrom django.utils import text\ntext.slugify(\"___This is a test ---\")\noutput: this-is-a-test\n\u200bPR\n", "patch": "diff --git a/django/utils/text.py b/django/utils/text.py\n--- a/django/utils/text.py\n+++ b/django/utils/text.py\n@@ -393,17 +393,18 @@ def unescape_string_literal(s):\n @keep_lazy_text\n def slugify(value, allow_unicode=False):\n \"\"\"\n- Convert to ASCII if 'allow_unicode' is False. Convert spaces to hyphens.\n- Remove characters that aren't alphanumerics, underscores, or hyphens.\n- Convert to lowercase. Also strip leading and trailing whitespace.\n+ Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated\n+ dashes to single dashes. Remove characters that aren't alphanumerics,\n+ underscores, or hyphens. Convert to lowercase. Also strip leading and\n+ trailing whitespace, dashes, and underscores.\n \"\"\"\n value = str(value)\n if allow_unicode:\n value = unicodedata.normalize('NFKC', value)\n else:\n value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')\n- value = re.sub(r'[^\\w\\s-]', '', value.lower()).strip()\n- return re.sub(r'[-\\s]+', '-', value)\n+ value = re.sub(r'[^\\w\\s-]', '', value.lower())\n+ return re.sub(r'[-\\s]+', '-', value).strip('-_')\n \n \n def camel_case_to_spaces(value):\n"} +{"instance_id": "django__django-13028", "file_changes": [{"file": "django/db/models/sql/query.py", "changes": {"edited_entities": ["django/db/models/sql/query.py:Query.check_filterable"], "edited_modules": ["django/db/models/sql/query.py:Query"]}}], "repo": "django/django", "base_commit": "78ad4b4b0201003792bfdbf1a7781cbc9ee03539", "problem_statement": "Queryset raises NotSupportedError when RHS has filterable=False attribute.\nDescription\n\t \n\t\t(last modified by Nicolas Baccelli)\n\t \nI'm migrating my app to django 3.0.7 and I hit a strange behavior using a model class with a field labeled filterable\nclass ProductMetaDataType(models.Model):\n\tlabel = models.CharField(max_length=255, unique=True, blank=False, null=False)\n\tfilterable = models.BooleanField(default=False, verbose_name=_(\"filterable\"))\n\tclass Meta:\n\t\tapp_label = \"adminpricing\"\n\t\tverbose_name = _(\"product meta data type\")\n\t\tverbose_name_plural = _(\"product meta data types\")\n\tdef __str__(self):\n\t\treturn self.label\nclass ProductMetaData(models.Model):\n\tid = models.BigAutoField(primary_key=True)\n\tproduct = models.ForeignKey(\n\t\tProduit, null=False, blank=False, on_delete=models.CASCADE\n\t)\n\tvalue = models.TextField(null=False, blank=False)\n\tmarketplace = models.ForeignKey(\n\t\tPlateforme, null=False, blank=False, on_delete=models.CASCADE\n\t)\n\tdate_created = models.DateTimeField(null=True, default=timezone.now)\n\tmetadata_type = models.ForeignKey(\n\t\tProductMetaDataType, null=False, blank=False, on_delete=models.CASCADE\n\t)\n\tclass Meta:\n\t\tapp_label = \"adminpricing\"\n\t\tverbose_name = _(\"product meta data\")\n\t\tverbose_name_plural = _(\"product meta datas\")\nError happened when filtering ProductMetaData with a metadata_type :\nProductMetaData.objects.filter(value=\"Dark Vador\", metadata_type=self.brand_metadata)\nError traceback :\nTraceback (most recent call last):\n File \"/backoffice/backoffice/adminpricing/tests/test_pw.py\", line 481, in test_checkpolicywarning_by_fields\n\tfor p in ProductMetaData.objects.filter(\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/manager.py\", line 82, in manager_method\n\treturn getattr(self.get_queryset(), name)(*args, **kwargs)\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/query.py\", line 904, in filter\n\treturn self._filter_or_exclude(False, *args, **kwargs)\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/query.py\", line 923, in _filter_or_exclude\n\tclone.query.add_q(Q(*args, **kwargs))\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py\", line 1351, in add_q\n\tclause, _ = self._add_q(q_object, self.used_aliases)\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py\", line 1378, in _add_q\n\tchild_clause, needed_inner = self.build_filter(\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py\", line 1264, in build_filter\n\tself.check_filterable(value)\n File \"/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py\", line 1131, in check_filterable\n\traise NotSupportedError(\ndjango.db.utils.NotSupportedError: ProductMetaDataType is disallowed in the filter clause.\nI changed label to filterable_test and it fixed this issue\nThis should be documented or fix.\n", "patch": "diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py\n--- a/django/db/models/sql/query.py\n+++ b/django/db/models/sql/query.py\n@@ -1124,7 +1124,10 @@ def check_related_objects(self, field, value, opts):\n \n def check_filterable(self, expression):\n \"\"\"Raise an error if expression cannot be used in a WHERE clause.\"\"\"\n- if not getattr(expression, 'filterable', True):\n+ if (\n+ hasattr(expression, 'resolve_expression') and\n+ not getattr(expression, 'filterable', True)\n+ ):\n raise NotSupportedError(\n expression.__class__.__name__ + ' is disallowed in the filter '\n 'clause.'\n"} +{"instance_id": "django__django-13033", "file_changes": [{"file": "django/db/models/sql/compiler.py", "changes": {"edited_entities": ["django/db/models/sql/compiler.py:SQLCompiler.find_ordering_name"], "edited_modules": ["django/db/models/sql/compiler.py:SQLCompiler"]}}], "repo": "django/django", "base_commit": "a59de6e89e8dc1f3e71c9a5a5bbceb373ea5247e", "problem_statement": "Self referencing foreign key doesn't correctly order by a relation \"_id\" field.\nDescription\n\t\nInitially discovered on 2.2.10 but verified still happens on 3.0.6. Given the following models:\nclass OneModel(models.Model):\n\tclass Meta:\n\t\tordering = (\"-id\",)\n\tid = models.BigAutoField(primary_key=True)\n\troot = models.ForeignKey(\"OneModel\", on_delete=models.CASCADE, null=True)\n\toneval = models.BigIntegerField(null=True)\nclass TwoModel(models.Model):\n\tid = models.BigAutoField(primary_key=True)\n\trecord = models.ForeignKey(OneModel, on_delete=models.CASCADE)\n\ttwoval = models.BigIntegerField(null=True)\nThe following queryset gives unexpected results and appears to be an incorrect SQL query:\nqs = TwoModel.objects.filter(record__oneval__in=[1,2,3])\nqs = qs.order_by(\"record__root_id\")\nprint(qs.query)\nSELECT \"orion_twomodel\".\"id\", \"orion_twomodel\".\"record_id\", \"orion_twomodel\".\"twoval\" FROM \"orion_twomodel\" INNER JOIN \"orion_onemodel\" ON (\"orion_twomodel\".\"record_id\" = \"orion_onemodel\".\"id\") LEFT OUTER JOIN \"orion_onemodel\" T3 ON (\"orion_onemodel\".\"root_id\" = T3.\"id\") WHERE \"orion_onemodel\".\"oneval\" IN (1, 2, 3) ORDER BY T3.\"id\" DESC\nThe query has an unexpected DESCENDING sort. That appears to come from the default sort order on the OneModel class, but I would expect the order_by() to take prececence. The the query has two JOINS, which is unnecessary. It appears that, since OneModel.root is a foreign key to itself, that is causing it to do the unnecessary extra join. In fact, testing a model where root is a foreign key to a third model doesn't show the problem behavior.\nNote also that the queryset with order_by(\"record__root\") gives the exact same SQL.\nThis queryset gives correct results and what looks like a pretty optimal SQL:\nqs = TwoModel.objects.filter(record__oneval__in=[1,2,3])\nqs = qs.order_by(\"record__root__id\")\nprint(qs.query)\nSELECT \"orion_twomodel\".\"id\", \"orion_twomodel\".\"record_id\", \"orion_twomodel\".\"twoval\" FROM \"orion_twomodel\" INNER JOIN \"orion_onemodel\" ON (\"orion_twomodel\".\"record_id\" = \"orion_onemodel\".\"id\") WHERE \"orion_onemodel\".\"oneval\" IN (1, 2, 3) ORDER BY \"orion_onemodel\".\"root_id\" ASC\nSo is this a potential bug or a misunderstanding on my part?\nAnother queryset that works around the issue and gives a reasonable SQL query and expected results:\nqs = TwoModel.objects.filter(record__oneval__in=[1,2,3])\nqs = qs.annotate(root_id=F(\"record__root_id\"))\nqs = qs.order_by(\"root_id\")\nprint(qs.query)\nSELECT \"orion_twomodel\".\"id\", \"orion_twomodel\".\"record_id\", \"orion_twomodel\".\"twoval\" FROM \"orion_twomodel\" INNER JOIN \"orion_onemodel\" ON (\"orion_twomodel\".\"record_id\" = \"orion_onemodel\".\"id\") WHERE \"orion_onemodel\".\"oneval\" IN (1, 2, 3) ORDER BY \"orion_onemodel\".\"zero_id\" ASC\nASCENDING sort, and a single INNER JOIN, as I'd expect. That actually works for my use because I need that output column anyway.\nOne final oddity; with the original queryset but the inverted sort order_by():\nqs = TwoModel.objects.filter(record__oneval__in=[1,2,3])\nqs = qs.order_by(\"-record__root_id\")\nprint(qs.query)\nSELECT \"orion_twomodel\".\"id\", \"orion_twomodel\".\"record_id\", \"orion_twomodel\".\"twoval\" FROM \"orion_twomodel\" INNER JOIN \"orion_onemodel\" ON (\"orion_twomodel\".\"record_id\" = \"orion_onemodel\".\"id\") LEFT OUTER JOIN \"orion_onemodel\" T3 ON (\"orion_onemodel\".\"root_id\" = T3.\"id\") WHERE \"orion_onemodel\".\"oneval\" IN (1, 2, 3) ORDER BY T3.\"id\" ASC\nOne gets the query with the two JOINs but an ASCENDING sort order. I was not under the impression that sort orders are somehow relative to the class level sort order, eg: does specifing order_by(\"-record__root_id\") invert the class sort order? Testing that on a simple case doesn't show that behavior at all.\nThanks for any assistance and clarification.\n", "patch": "diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py\n--- a/django/db/models/sql/compiler.py\n+++ b/django/db/models/sql/compiler.py\n@@ -727,7 +727,12 @@ def find_ordering_name(self, name, opts, alias=None, default_order='ASC',\n # If we get to this point and the field is a relation to another model,\n # append the default ordering for that model unless it is the pk\n # shortcut or the attribute name of the field that is specified.\n- if field.is_relation and opts.ordering and getattr(field, 'attname', None) != name and name != 'pk':\n+ if (\n+ field.is_relation and\n+ opts.ordering and\n+ getattr(field, 'attname', None) != pieces[-1] and\n+ name != 'pk'\n+ ):\n # Firstly, avoid infinite loops.\n already_seen = already_seen or set()\n join_tuple = tuple(getattr(self.query.alias_map[j], 'join_cols', None) for j in joins)\n"} +{"instance_id": "django__django-13158", "file_changes": [{"file": "django/db/models/sql/query.py", "changes": {"edited_entities": ["django/db/models/sql/query.py:Query.clone", "django/db/models/sql/query.py:Query.set_empty"], "edited_modules": ["django/db/models/sql/query.py:Query"]}}], "repo": "django/django", "base_commit": "7af8f4127397279d19ef7c7899e93018274e2f9b", "problem_statement": "QuerySet.none() on combined queries returns all results.\nDescription\n\t\nI came across this issue on Stack Overflow. I'm not 100% sure it's a bug, but it does seem strange. With this code (excuse the bizarre example filtering):\nclass Publication(models.Model):\n\tpass\nclass Article(models.Model):\n\tpublications = models.ManyToManyField(to=Publication, blank=True, null=True)\nclass ArticleForm(forms.ModelForm):\n\tpublications = forms.ModelMultipleChoiceField(\n\t\tPublication.objects.filter(id__lt=2) | Publication.objects.filter(id__gt=5),\n\t\trequired=False,\n\t)\n\tclass Meta:\n\t\tmodel = Article\n\t\tfields = [\"publications\"]\nclass ArticleAdmin(admin.ModelAdmin):\n\tform = ArticleForm\nThis works well. However, changing the ModelMultipleChoiceField queryset to use union() breaks things.\npublications = forms.ModelMultipleChoiceField(\n\tPublication.objects.filter(id__lt=2).union(\n\t\tPublication.objects.filter(id__gt=5)\n\t),\n\trequired=False,\n)\nThe form correctly shows only the matching objects. However, if you submit this form while empty (i.e. you didn't select any publications), ALL objects matching the queryset will be added. Using the OR query, NO objects are added, as I'd expect.\n", "patch": "diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py\n--- a/django/db/models/sql/query.py\n+++ b/django/db/models/sql/query.py\n@@ -305,6 +305,7 @@ def clone(self):\n obj.annotation_select_mask = None\n else:\n obj.annotation_select_mask = self.annotation_select_mask.copy()\n+ obj.combined_queries = tuple(query.clone() for query in self.combined_queries)\n # _annotation_select_cache cannot be copied, as doing so breaks the\n # (necessary) state in which both annotations and\n # _annotation_select_cache point to the same underlying objects.\n@@ -1777,6 +1778,8 @@ def split_exclude(self, filter_expr, can_reuse, names_with_path):\n \n def set_empty(self):\n self.where.add(NothingNode(), AND)\n+ for query in self.combined_queries:\n+ query.set_empty()\n \n def is_empty(self):\n return any(isinstance(c, NothingNode) for c in self.where.children)\n"} +{"instance_id": "django__django-13220", "file_changes": [{"file": "django/core/exceptions.py", "changes": {"added_entities": ["django/core/exceptions.py:ValidationError.__eq__", "django/core/exceptions.py:ValidationError.__hash__"], "added_modules": ["django/core/exceptions.py:ValidationError"]}}], "repo": "django/django", "base_commit": "16218c20606d8cd89c5393970c83da04598a3e04", "problem_statement": "Allow ValidationErrors to equal each other when created identically\nDescription\n\t \n\t\t(last modified by kamni)\n\t \nCurrently ValidationErrors (django.core.exceptions.ValidationError) that have identical messages don't equal each other, which is counter-intuitive, and can make certain kinds of testing more complicated. Please add an __eq__ method that allows two ValidationErrors to be compared. \nIdeally, this would be more than just a simple self.messages == other.messages. It would be most helpful if the comparison were independent of the order in which errors were raised in a field or in non_field_errors.\n", "patch": "diff --git a/django/core/exceptions.py b/django/core/exceptions.py\n--- a/django/core/exceptions.py\n+++ b/django/core/exceptions.py\n@@ -1,6 +1,9 @@\n \"\"\"\n Global Django exception and warning classes.\n \"\"\"\n+import operator\n+\n+from django.utils.hashable import make_hashable\n \n \n class FieldDoesNotExist(Exception):\n@@ -182,6 +185,23 @@ def __str__(self):\n def __repr__(self):\n return 'ValidationError(%s)' % self\n \n+ def __eq__(self, other):\n+ if not isinstance(other, ValidationError):\n+ return NotImplemented\n+ return hash(self) == hash(other)\n+\n+ def __hash__(self):\n+ # Ignore params and messages ordering.\n+ if hasattr(self, 'message'):\n+ return hash((\n+ self.message,\n+ self.code,\n+ tuple(sorted(make_hashable(self.params))) if self.params else None,\n+ ))\n+ if hasattr(self, 'error_dict'):\n+ return hash(tuple(sorted(make_hashable(self.error_dict))))\n+ return hash(tuple(sorted(self.error_list, key=operator.attrgetter('message'))))\n+\n \n class EmptyResultSet(Exception):\n \"\"\"A database query predicate is impossible.\"\"\"\n"} +{"instance_id": "django__django-13230", "file_changes": [{"file": "django/contrib/syndication/views.py", "changes": {"edited_entities": ["django/contrib/syndication/views.py:Feed.get_feed"], "edited_modules": ["django/contrib/syndication/views.py:Feed"]}}], "repo": "django/django", "base_commit": "184a6eebb0ef56d5f1b1315a8e666830e37f3f81", "problem_statement": "Add support for item_comments to syndication framework\nDescription\n\t\nAdd comments argument to feed.add_item() in syndication.views so that item_comments can be defined directly without having to take the detour via item_extra_kwargs .\nAdditionally, comments is already explicitly mentioned in the feedparser, but not implemented in the view.\n", "patch": "diff --git a/django/contrib/syndication/views.py b/django/contrib/syndication/views.py\n--- a/django/contrib/syndication/views.py\n+++ b/django/contrib/syndication/views.py\n@@ -212,6 +212,7 @@ def get_feed(self, obj, request):\n author_name=author_name,\n author_email=author_email,\n author_link=author_link,\n+ comments=self._get_dynamic_attr('item_comments', item),\n categories=self._get_dynamic_attr('item_categories', item),\n item_copyright=self._get_dynamic_attr('item_copyright', item),\n **self.item_extra_kwargs(item)\n"} +{"instance_id": "django__django-13265", "file_changes": [{"file": "django/db/migrations/autodetector.py", "changes": {"edited_entities": ["django/db/migrations/autodetector.py:MigrationAutodetector._detect_changes", "django/db/migrations/autodetector.py:MigrationAutodetector.generate_created_models"], "edited_modules": ["django/db/migrations/autodetector.py:MigrationAutodetector"]}}], "repo": "django/django", "base_commit": "b2b0711b555fa292751763c2df4fe577c396f265", "problem_statement": "AlterOrderWithRespectTo() with ForeignKey crash when _order is included in Index().\nDescription\n\t\n\tclass Meta:\n\t\tdb_table = 'look_image'\n\t\torder_with_respect_to = 'look'\n\t\tindexes = [\n\t\t\tmodels.Index(fields=['look', '_order']),\n\t\t\tmodels.Index(fields=['created_at']),\n\t\t\tmodels.Index(fields=['updated_at']),\n\t\t]\nmigrations.CreateModel(\n\t\t\tname='LookImage',\n\t\t\tfields=[\n\t\t\t\t('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n\t\t\t\t('look', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='posts.Look', verbose_name='LOOK')),\n\t\t\t\t('image_url', models.URLField(blank=True, max_length=10000, null=True)),\n\t\t\t\t('image', models.ImageField(max_length=2000, upload_to='')),\n\t\t\t\t('deleted', models.DateTimeField(editable=False, null=True)),\n\t\t\t\t('created_at', models.DateTimeField(auto_now_add=True)),\n\t\t\t\t('updated_at', models.DateTimeField(auto_now=True)),\n\t\t\t],\n\t\t),\n\t\tmigrations.AddIndex(\n\t\t\tmodel_name='lookimage',\n\t\t\tindex=models.Index(fields=['look', '_order'], name='look_image_look_id_eaff30_idx'),\n\t\t),\n\t\tmigrations.AddIndex(\n\t\t\tmodel_name='lookimage',\n\t\t\tindex=models.Index(fields=['created_at'], name='look_image_created_f746cf_idx'),\n\t\t),\n\t\tmigrations.AddIndex(\n\t\t\tmodel_name='lookimage',\n\t\t\tindex=models.Index(fields=['updated_at'], name='look_image_updated_aceaf9_idx'),\n\t\t),\n\t\tmigrations.AlterOrderWithRespectTo(\n\t\t\tname='lookimage',\n\t\t\torder_with_respect_to='look',\n\t\t),\nI added orders_with_respect_to in new model class's Meta class and also made index for '_order' field by combining with other field. And a new migration file based on the model looks like the code above.\nThe problem is operation AlterOrderWithRespectTo after AddIndex of '_order' raising error because '_order' field had not been created yet.\nIt seems to be AlterOrderWithRespectTo has to proceed before AddIndex of '_order'.\n", "patch": "diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py\n--- a/django/db/migrations/autodetector.py\n+++ b/django/db/migrations/autodetector.py\n@@ -182,12 +182,12 @@ def _detect_changes(self, convert_apps=None, graph=None):\n self.generate_removed_fields()\n self.generate_added_fields()\n self.generate_altered_fields()\n+ self.generate_altered_order_with_respect_to()\n self.generate_altered_unique_together()\n self.generate_altered_index_together()\n self.generate_added_indexes()\n self.generate_added_constraints()\n self.generate_altered_db_table()\n- self.generate_altered_order_with_respect_to()\n \n self._sort_migrations()\n self._build_migration_list(graph)\n@@ -613,6 +613,18 @@ def generate_created_models(self):\n dependencies=list(set(dependencies)),\n )\n # Generate other opns\n+ if order_with_respect_to:\n+ self.add_operation(\n+ app_label,\n+ operations.AlterOrderWithRespectTo(\n+ name=model_name,\n+ order_with_respect_to=order_with_respect_to,\n+ ),\n+ dependencies=[\n+ (app_label, model_name, order_with_respect_to, True),\n+ (app_label, model_name, None, True),\n+ ]\n+ )\n related_dependencies = [\n (app_label, model_name, name, True)\n for name in sorted(related_fields)\n@@ -654,19 +666,6 @@ def generate_created_models(self):\n ),\n dependencies=related_dependencies\n )\n- if order_with_respect_to:\n- self.add_operation(\n- app_label,\n- operations.AlterOrderWithRespectTo(\n- name=model_name,\n- order_with_respect_to=order_with_respect_to,\n- ),\n- dependencies=[\n- (app_label, model_name, order_with_respect_to, True),\n- (app_label, model_name, None, True),\n- ]\n- )\n-\n # Fix relationships if the model changed from a proxy model to a\n # concrete model.\n if (app_label, model_name) in self.old_proxy_keys:\n"} +{"instance_id": "django__django-13315", "file_changes": [{"file": "django/forms/models.py", "changes": {"edited_entities": ["django/forms/models.py:apply_limit_choices_to_to_formfield"], "edited_modules": ["django/forms/models.py:apply_limit_choices_to_to_formfield"]}}], "repo": "django/django", "base_commit": "36bc47069ce071e80c8129500de3b8664d2058a7", "problem_statement": "limit_choices_to on a ForeignKey can render duplicate options in formfield\nDescription\n\t\nIf you pass a Q object as limit_choices_to on a ForeignKey field involving a join, you may end up with duplicate options in your form.\nSee regressiontest in patch for a clear view on the problem.\n", "patch": "diff --git a/django/forms/models.py b/django/forms/models.py\n--- a/django/forms/models.py\n+++ b/django/forms/models.py\n@@ -97,10 +97,18 @@ def model_to_dict(instance, fields=None, exclude=None):\n \n def apply_limit_choices_to_to_formfield(formfield):\n \"\"\"Apply limit_choices_to to the formfield's queryset if needed.\"\"\"\n+ from django.db.models import Exists, OuterRef, Q\n if hasattr(formfield, 'queryset') and hasattr(formfield, 'get_limit_choices_to'):\n limit_choices_to = formfield.get_limit_choices_to()\n- if limit_choices_to is not None:\n- formfield.queryset = formfield.queryset.complex_filter(limit_choices_to)\n+ if limit_choices_to:\n+ complex_filter = limit_choices_to\n+ if not isinstance(complex_filter, Q):\n+ complex_filter = Q(**limit_choices_to)\n+ complex_filter &= Q(pk=OuterRef('pk'))\n+ # Use Exists() to avoid potential duplicates.\n+ formfield.queryset = formfield.queryset.filter(\n+ Exists(formfield.queryset.model._base_manager.filter(complex_filter)),\n+ )\n \n \n def fields_for_model(model, fields=None, exclude=None, widgets=None,\n"} +{"instance_id": "django__django-13321", "file_changes": [{"file": "django/contrib/sessions/backends/base.py", "changes": {"edited_entities": ["django/contrib/sessions/backends/base.py:SessionBase.decode"], "edited_modules": ["django/contrib/sessions/backends/base.py:SessionBase"]}}], "repo": "django/django", "base_commit": "35b03788b0607c1f8d2b64e4fa9e1669b0907ea4", "problem_statement": "Decoding an invalid session data crashes.\nDescription\n\t \n\t\t(last modified by Matt Hegarty)\n\t \nHi\nI recently upgraded my staging server to 3.1. I think that there was an old session which was still active.\nOn browsing to any URL, I get the crash below. It looks similar to \u200bthis issue.\nI cannot login at all with Chrome - each attempt to access the site results in a crash. Login with Firefox works fine.\nThis is only happening on my Staging site, which is running Gunicorn behind nginx proxy.\nInternal Server Error: /overview/\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 215, in _get_session\nreturn self._session_cache\nAttributeError: 'SessionStore' object has no attribute '_session_cache'\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 118, in decode\nreturn signing.loads(session_data, salt=self.key_salt, serializer=self.serializer)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 135, in loads\nbase64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age).encode()\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 201, in unsign\nresult = super().unsign(value)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/signing.py\", line 184, in unsign\nraise BadSignature('Signature \"%s\" does not match' % sig)\ndjango.core.signing.BadSignature: Signature \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\" does not match\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\nFile \"/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py\", line 47, in inner\nresponse = get_response(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py\", line 179, in _get_response\nresponse = wrapped_callback(request, *callback_args, **callback_kwargs)\nFile \"/usr/local/lib/python3.8/site-packages/django/views/generic/base.py\", line 73, in view\nreturn self.dispatch(request, *args, **kwargs)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/mixins.py\", line 50, in dispatch\nif not request.user.is_authenticated:\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 240, in inner\nself._setup()\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 376, in _setup\nself._wrapped = self._setupfunc()\nFile \"/usr/local/lib/python3.8/site-packages/django_otp/middleware.py\", line 38, in _verify_user\nuser.otp_device = None\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 270, in __setattr__\nself._setup()\nFile \"/usr/local/lib/python3.8/site-packages/django/utils/functional.py\", line 376, in _setup\nself._wrapped = self._setupfunc()\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/middleware.py\", line 23, in \nrequest.user = SimpleLazyObject(lambda: get_user(request))\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/middleware.py\", line 11, in get_user\nrequest._cached_user = auth.get_user(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py\", line 174, in get_user\nuser_id = _get_user_session_key(request)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py\", line 58, in _get_user_session_key\nreturn get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 65, in __getitem__\nreturn self._session[key]\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 220, in _get_session\nself._session_cache = self.load()\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/db.py\", line 44, in load\nreturn self.decode(s.session_data) if s else {}\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 122, in decode\nreturn self._legacy_decode(session_data)\nFile \"/usr/local/lib/python3.8/site-packages/django/contrib/sessions/backends/base.py\", line 126, in _legacy_decode\nencoded_data = base64.b64decode(session_data.encode('ascii'))\nFile \"/usr/local/lib/python3.8/base64.py\", line 87, in b64decode\nreturn binascii.a2b_base64(s)\nbinascii.Error: Incorrect padding\n", "patch": "diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py\n--- a/django/contrib/sessions/backends/base.py\n+++ b/django/contrib/sessions/backends/base.py\n@@ -121,6 +121,15 @@ def decode(self, session_data):\n return signing.loads(session_data, salt=self.key_salt, serializer=self.serializer)\n # RemovedInDjango40Warning: when the deprecation ends, handle here\n # exceptions similar to what _legacy_decode() does now.\n+ except signing.BadSignature:\n+ try:\n+ # Return an empty session if data is not in the pre-Django 3.1\n+ # format.\n+ return self._legacy_decode(session_data)\n+ except Exception:\n+ logger = logging.getLogger('django.security.SuspiciousSession')\n+ logger.warning('Session data corrupted')\n+ return {}\n except Exception:\n return self._legacy_decode(session_data)\n \n"} +{"instance_id": "django__django-13401", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {"edited_entities": ["django/db/models/fields/__init__.py:Field.__eq__", "django/db/models/fields/__init__.py:Field.__lt__", "django/db/models/fields/__init__.py:Field.__hash__"], "edited_modules": ["django/db/models/fields/__init__.py:Field"]}}], "repo": "django/django", "base_commit": "453967477e3ddae704cd739eac2449c0e13d464c", "problem_statement": "Abstract model field should not be equal across models\nDescription\n\t\nConsider the following models:\nclass A(models.Model):\n\tclass Meta:\n\t\tabstract = True\n\tmyfield = IntegerField()\nclass B(A):\n\tpass\nclass C(A):\n\tpass\nIf I pull the fields of B and C into a shared set, one will be de-duplicated away, because they compare as equal. I found this surprising, though in practice using a list was sufficient for my need. The root of the issue is that they compare equal, as fields only consider self.creation_counter when comparing for equality.\nlen({B._meta.get_field('myfield'), C._meta.get_field('myfield')}) == 1\nB._meta.get_field('myfield') == C._meta.get_field('myfield')\nWe should adjust __eq__ so that if the field.model is different, they will compare unequal. Similarly, it is probably wise to adjust __hash__ and __lt__ to match.\nWhen adjusting __lt__, it may be wise to order first by self.creation_counter so that cases not affected by this equality collision won't be re-ordered. In my experimental branch, there was one test that broke if I ordered them by model first.\nI brought this up on IRC django-dev to check my intuitions, and those conversing with me there seemed to agree that the current behavior is not intuitive.\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -516,17 +516,37 @@ def clone(self):\n def __eq__(self, other):\n # Needed for @total_ordering\n if isinstance(other, Field):\n- return self.creation_counter == other.creation_counter\n+ return (\n+ self.creation_counter == other.creation_counter and\n+ getattr(self, 'model', None) == getattr(other, 'model', None)\n+ )\n return NotImplemented\n \n def __lt__(self, other):\n # This is needed because bisect does not take a comparison function.\n+ # Order by creation_counter first for backward compatibility.\n if isinstance(other, Field):\n- return self.creation_counter < other.creation_counter\n+ if (\n+ self.creation_counter != other.creation_counter or\n+ not hasattr(self, 'model') and not hasattr(other, 'model')\n+ ):\n+ return self.creation_counter < other.creation_counter\n+ elif hasattr(self, 'model') != hasattr(other, 'model'):\n+ return not hasattr(self, 'model') # Order no-model fields first\n+ else:\n+ # creation_counter's are equal, compare only models.\n+ return (\n+ (self.model._meta.app_label, self.model._meta.model_name) <\n+ (other.model._meta.app_label, other.model._meta.model_name)\n+ )\n return NotImplemented\n \n def __hash__(self):\n- return hash(self.creation_counter)\n+ return hash((\n+ self.creation_counter,\n+ self.model._meta.app_label if hasattr(self, 'model') else None,\n+ self.model._meta.model_name if hasattr(self, 'model') else None,\n+ ))\n \n def __deepcopy__(self, memodict):\n # We don't have to deepcopy very much here, since most things are not\n"} +{"instance_id": "django__django-13447", "file_changes": [{"file": "django/contrib/admin/sites.py", "changes": {"edited_entities": ["django/contrib/admin/sites.py:AdminSite._build_app_dict"], "edited_modules": ["django/contrib/admin/sites.py:AdminSite"]}}], "repo": "django/django", "base_commit": "0456d3e42795481a186db05719300691fe2a1029", "problem_statement": "Added model class to app_list context\nDescription\n\t \n\t\t(last modified by Raffaele Salmaso)\n\t \nI need to manipulate the app_list in my custom admin view, and the easiest way to get the result is to have access to the model class (currently the dictionary is a serialized model).\nIn addition I would make the _build_app_dict method public, as it is used by the two views index and app_index.\n", "patch": "diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py\n--- a/django/contrib/admin/sites.py\n+++ b/django/contrib/admin/sites.py\n@@ -461,6 +461,7 @@ def _build_app_dict(self, request, label=None):\n \n info = (app_label, model._meta.model_name)\n model_dict = {\n+ 'model': model,\n 'name': capfirst(model._meta.verbose_name_plural),\n 'object_name': model._meta.object_name,\n 'perms': perms,\n"} +{"instance_id": "django__django-13448", "file_changes": [{"file": "django/db/backends/base/creation.py", "changes": {"edited_entities": ["django/db/backends/base/creation.py:BaseDatabaseCreation.create_test_db"], "edited_modules": ["django/db/backends/base/creation.py:BaseDatabaseCreation"]}}], "repo": "django/django", "base_commit": "7b9596b974fb0ad1868b10c8c2174e10b72be403", "problem_statement": "Test runner setup_databases crashes with \"TEST\": {\"MIGRATE\": False}.\nDescription\n\t\nI'm trying to upgrade a project from Django 3.0 to Django 3.1 and wanted to try out the new \"TEST\": {\"MIGRATE\": False} database setting.\nSadly I'm running into an issue immediately when running ./manage.py test.\nRemoving the \"TEST\": {\"MIGRATE\": False} line allows the tests to run. So this is not blocking the upgrade for us, but it would be nice if we were able to use the new feature to skip migrations during testing.\nFor reference, this project was recently upgraded from Django 1.4 all the way to 3.0 so there might be some legacy cruft somewhere that triggers this.\nHere's the trackeback. I'll try to debug this some more.\nTraceback (most recent call last):\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\npsycopg2.errors.UndefinedTable: relation \"django_admin_log\" does not exist\nLINE 1: ...n_flag\", \"django_admin_log\".\"change_message\" FROM \"django_ad...\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ^\nThe above exception was the direct cause of the following exception:\nTraceback (most recent call last):\n File \"/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py\", line 1156, in execute_sql\n\tcursor.execute(sql, params)\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py\", line 66, in execute\n\treturn self._execute_with_wrappers(sql, params, many=False, executor=self._execute)\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py\", line 75, in _execute_with_wrappers\n\treturn executor(sql, params, many, context)\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"/usr/local/lib/python3.6/site-packages/django/db/utils.py\", line 90, in __exit__\n\traise dj_exc_value.with_traceback(traceback) from exc_value\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\ndjango.db.utils.ProgrammingError: relation \"django_admin_log\" does not exist\nLINE 1: ...n_flag\", \"django_admin_log\".\"change_message\" FROM \"django_ad...\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ^\nDuring handling of the above exception, another exception occurred:\nTraceback (most recent call last):\n File \"./manage.py\", line 15, in \n\tmain()\n File \"./manage.py\", line 11, in main\n\texecute_from_command_line(sys.argv)\n File \"/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py\", line 401, in execute_from_command_line\n\tutility.execute()\n File \"/usr/local/lib/python3.6/site-packages/django/core/management/__init__.py\", line 395, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"/usr/local/lib/python3.6/site-packages/django/core/management/commands/test.py\", line 23, in run_from_argv\n\tsuper().run_from_argv(argv)\n File \"/usr/local/lib/python3.6/site-packages/django/core/management/base.py\", line 330, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"/usr/local/lib/python3.6/site-packages/django/core/management/base.py\", line 371, in execute\n\toutput = self.handle(*args, **options)\n File \"/usr/local/lib/python3.6/site-packages/django/core/management/commands/test.py\", line 53, in handle\n\tfailures = test_runner.run_tests(test_labels)\n File \"/usr/local/lib/python3.6/site-packages/django/test/runner.py\", line 695, in run_tests\n\told_config = self.setup_databases(aliases=databases)\n File \"/usr/local/lib/python3.6/site-packages/django/test/runner.py\", line 616, in setup_databases\n\tself.parallel, **kwargs\n File \"/usr/local/lib/python3.6/site-packages/django/test/utils.py\", line 174, in setup_databases\n\tserialize=connection.settings_dict['TEST'].get('SERIALIZE', True),\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/base/creation.py\", line 78, in create_test_db\n\tself.connection._test_serialized_contents = self.serialize_db_to_string()\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/base/creation.py\", line 121, in serialize_db_to_string\n\tserializers.serialize(\"json\", get_objects(), indent=None, stream=out)\n File \"/usr/local/lib/python3.6/site-packages/django/core/serializers/__init__.py\", line 128, in serialize\n\ts.serialize(queryset, **options)\n File \"/usr/local/lib/python3.6/site-packages/django/core/serializers/base.py\", line 90, in serialize\n\tfor count, obj in enumerate(queryset, start=1):\n File \"/usr/local/lib/python3.6/site-packages/django/db/backends/base/creation.py\", line 118, in get_objects\n\tyield from queryset.iterator()\n File \"/usr/local/lib/python3.6/site-packages/django/db/models/query.py\", line 360, in _iterator\n\tyield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size)\n File \"/usr/local/lib/python3.6/site-packages/django/db/models/query.py\", line 53, in __iter__\n\tresults = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)\n File \"/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py\", line 1159, in execute_sql\n\tcursor.close()\npsycopg2.errors.InvalidCursorName: cursor \"_django_curs_139860821038912_sync_1\" does not exist\n", "patch": "diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py\n--- a/django/db/backends/base/creation.py\n+++ b/django/db/backends/base/creation.py\n@@ -58,7 +58,14 @@ def create_test_db(self, verbosity=1, autoclobber=False, serialize=True, keepdb=\n settings.DATABASES[self.connection.alias][\"NAME\"] = test_database_name\n self.connection.settings_dict[\"NAME\"] = test_database_name\n \n- if self.connection.settings_dict['TEST']['MIGRATE']:\n+ try:\n+ if self.connection.settings_dict['TEST']['MIGRATE'] is False:\n+ # Disable migrations for all apps.\n+ old_migration_modules = settings.MIGRATION_MODULES\n+ settings.MIGRATION_MODULES = {\n+ app.label: None\n+ for app in apps.get_app_configs()\n+ }\n # We report migrate messages at one level lower than that\n # requested. This ensures we don't get flooded with messages during\n # testing (unless you really ask to be flooded).\n@@ -69,6 +76,9 @@ def create_test_db(self, verbosity=1, autoclobber=False, serialize=True, keepdb=\n database=self.connection.alias,\n run_syncdb=True,\n )\n+ finally:\n+ if self.connection.settings_dict['TEST']['MIGRATE'] is False:\n+ settings.MIGRATION_MODULES = old_migration_modules\n \n # We then serialize the current state of the database into a string\n # and store it on the connection. This slightly horrific process is so people\n"} +{"instance_id": "django__django-13551", "file_changes": [{"file": "django/contrib/auth/tokens.py", "changes": {"edited_entities": ["django/contrib/auth/tokens.py:PasswordResetTokenGenerator._make_hash_value"], "edited_modules": ["django/contrib/auth/tokens.py:PasswordResetTokenGenerator"]}}], "repo": "django/django", "base_commit": "7f9e4524d6b23424cf44fbe1bf1f4e70f6bb066e", "problem_statement": "Changing user's email could invalidate password reset tokens\nDescription\n\t\nSequence:\nHave account with email address foo@\u2026\nPassword reset request for that email (unused)\nfoo@\u2026 account changes their email address\nPassword reset email is used\nThe password reset email's token should be rejected at that point, but in fact it is allowed.\nThe fix is to add the user's email address into \u200bPasswordResetTokenGenerator._make_hash_value()\nNothing forces a user to even have an email as per AbstractBaseUser. Perhaps the token generation method could be factored out onto the model, ala get_session_auth_hash().\n", "patch": "diff --git a/django/contrib/auth/tokens.py b/django/contrib/auth/tokens.py\n--- a/django/contrib/auth/tokens.py\n+++ b/django/contrib/auth/tokens.py\n@@ -78,9 +78,9 @@ def _make_token_with_timestamp(self, user, timestamp, legacy=False):\n \n def _make_hash_value(self, user, timestamp):\n \"\"\"\n- Hash the user's primary key and some user state that's sure to change\n- after a password reset to produce a token that invalidated when it's\n- used:\n+ Hash the user's primary key, email (if available), and some user state\n+ that's sure to change after a password reset to produce a token that is\n+ invalidated when it's used:\n 1. The password field will change upon a password reset (even if the\n same password is chosen, due to password salting).\n 2. The last_login field will usually be updated very shortly after\n@@ -94,7 +94,9 @@ def _make_hash_value(self, user, timestamp):\n # Truncate microseconds so that tokens are consistent even if the\n # database doesn't support microseconds.\n login_timestamp = '' if user.last_login is None else user.last_login.replace(microsecond=0, tzinfo=None)\n- return str(user.pk) + user.password + str(login_timestamp) + str(timestamp)\n+ email_field = user.get_email_field_name()\n+ email = getattr(user, email_field, '') or ''\n+ return f'{user.pk}{user.password}{login_timestamp}{timestamp}{email}'\n \n def _num_seconds(self, dt):\n return int((dt - datetime(2001, 1, 1)).total_seconds())\n"} +{"instance_id": "django__django-13590", "file_changes": [{"file": "django/db/models/sql/query.py", "changes": {"edited_entities": ["django/db/models/sql/query.py:Query.resolve_lookup_value"], "edited_modules": ["django/db/models/sql/query.py:Query"]}}], "repo": "django/django", "base_commit": "755dbf39fcdc491fe9b588358303e259c7750be4", "problem_statement": "Upgrading 2.2>3.0 causes named tuples used as arguments to __range to error.\nDescription\n\t\nI noticed this while upgrading a project from 2.2 to 3.0.\nThis project passes named 2-tuples as arguments to range queryset filters. This works fine on 2.2. On 3.0 it causes the following error: TypeError: __new__() missing 1 required positional argument: 'far'.\nThis happens because django.db.models.sql.query.Query.resolve_lookup_value goes into the tuple elements to resolve lookups and then attempts to reconstitute the tuple with the resolved elements.\nWhen it attempts to construct the new tuple it preserves the type (the named tuple) but it passes a iterator to it's constructor.\nNamedTuples don't have the code path for copying an iterator, and so it errors on insufficient arguments.\nThe fix is to * expand the contents of the iterator into the constructor.\n", "patch": "diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py\n--- a/django/db/models/sql/query.py\n+++ b/django/db/models/sql/query.py\n@@ -1077,10 +1077,14 @@ def resolve_lookup_value(self, value, can_reuse, allow_joins):\n elif isinstance(value, (list, tuple)):\n # The items of the iterable may be expressions and therefore need\n # to be resolved independently.\n- return type(value)(\n+ values = (\n self.resolve_lookup_value(sub_value, can_reuse, allow_joins)\n for sub_value in value\n )\n+ type_ = type(value)\n+ if hasattr(type_, '_make'): # namedtuple\n+ return type_(*values)\n+ return type_(values)\n return value\n \n def solve_lookup_type(self, lookup):\n"} +{"instance_id": "django__django-13658", "file_changes": [{"file": "django/core/management/__init__.py", "changes": {"edited_entities": ["django/core/management/__init__.py:ManagementUtility.execute"], "edited_modules": ["django/core/management/__init__.py:ManagementUtility"]}}], "repo": "django/django", "base_commit": "0773837e15bb632afffb6848a58c59a791008fa1", "problem_statement": "ManagementUtility instantiates CommandParser without passing already-computed prog argument\nDescription\n\t\nManagementUtility \u200bgoes to the trouble to parse the program name from the argv it's passed rather than from sys.argv: \n\tdef __init__(self, argv=None):\n\t\tself.argv = argv or sys.argv[:]\n\t\tself.prog_name = os.path.basename(self.argv[0])\n\t\tif self.prog_name == '__main__.py':\n\t\t\tself.prog_name = 'python -m django'\nBut then when it needs to parse --pythonpath and --settings, it \u200buses the program name from sys.argv: \n\t\tparser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)\nAbove \"%(prog)s\" \u200brefers to sys.argv[0]. Instead, it should refer to self.prog_name. This can fixed as follows:\n\t\tparser = CommandParser(\n\t\t\tprog=self.prog_name,\n\t\t\tusage='%(prog)s subcommand [options] [args]',\n\t\t\tadd_help=False,\n\t\t\tallow_abbrev=False)\nI'm aware that execute_from_command_line is a private API, but it'd be really convenient for me if it worked properly in my weird embedded environment where sys.argv[0] is \u200bincorrectly None. If passing my own argv to execute_from_command_line avoided all the ensuing exceptions, I wouldn't have to modify sys.argv[0] globally as I'm doing in the meantime.\n", "patch": "diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py\n--- a/django/core/management/__init__.py\n+++ b/django/core/management/__init__.py\n@@ -344,7 +344,12 @@ def execute(self):\n # Preprocess options to extract --settings and --pythonpath.\n # These options could affect the commands that are available, so they\n # must be processed early.\n- parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)\n+ parser = CommandParser(\n+ prog=self.prog_name,\n+ usage='%(prog)s subcommand [options] [args]',\n+ add_help=False,\n+ allow_abbrev=False,\n+ )\n parser.add_argument('--settings')\n parser.add_argument('--pythonpath')\n parser.add_argument('args', nargs='*') # catch-all\n"} +{"instance_id": "django__django-13660", "file_changes": [{"file": "django/core/management/commands/shell.py", "changes": {"edited_entities": ["django/core/management/commands/shell.py:Command.handle"], "edited_modules": ["django/core/management/commands/shell.py:Command"]}}], "repo": "django/django", "base_commit": "50c3ac6fa9b7c8a94a6d1dc87edf775e3bc4d575", "problem_statement": "shell command crashes when passing (with -c) the python code with functions.\nDescription\n\t\nThe examples below use Python 3.7 and Django 2.2.16, but I checked that the code is the same on master and works the same in Python 3.8.\nHere's how \u200bpython -c works:\n$ python -c <\n\tmanagement.execute_from_command_line()\n File \"{sys.prefix}/lib/python3.7/site-packages/django/core/management/__init__.py\", line 381, in execute_from_command_line\n\tutility.execute()\n File \"{sys.prefix}/lib/python3.7/site-packages/django/core/management/__init__.py\", line 375, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"{sys.prefix}/lib/python3.7/site-packages/django/core/management/base.py\", line 323, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"{sys.prefix}/lib/python3.7/site-packages/django/core/management/base.py\", line 364, in execute\n\toutput = self.handle(*args, **options)\n File \"{sys.prefix}/lib/python3.7/site-packages/django/core/management/commands/shell.py\", line 86, in handle\n\texec(options['command'])\n File \"\", line 5, in \n File \"\", line 4, in f\nNameError: name 'django' is not defined\nThe problem is in the \u200busage of \u200bexec:\n\tdef handle(self, **options):\n\t\t# Execute the command and exit.\n\t\tif options['command']:\n\t\t\texec(options['command'])\n\t\t\treturn\n\t\t# Execute stdin if it has anything to read and exit.\n\t\t# Not supported on Windows due to select.select() limitations.\n\t\tif sys.platform != 'win32' and not sys.stdin.isatty() and select.select([sys.stdin], [], [], 0)[0]:\n\t\t\texec(sys.stdin.read())\n\t\t\treturn\nexec should be passed a dictionary containing a minimal set of globals. This can be done by just passing a new, empty dictionary as the second argument of exec.\n", "patch": "diff --git a/django/core/management/commands/shell.py b/django/core/management/commands/shell.py\n--- a/django/core/management/commands/shell.py\n+++ b/django/core/management/commands/shell.py\n@@ -84,13 +84,13 @@ def python(self, options):\n def handle(self, **options):\n # Execute the command and exit.\n if options['command']:\n- exec(options['command'])\n+ exec(options['command'], globals())\n return\n \n # Execute stdin if it has anything to read and exit.\n # Not supported on Windows due to select.select() limitations.\n if sys.platform != 'win32' and not sys.stdin.isatty() and select.select([sys.stdin], [], [], 0)[0]:\n- exec(sys.stdin.read())\n+ exec(sys.stdin.read(), globals())\n return\n \n available_shells = [options['interface']] if options['interface'] else self.shells\n"} +{"instance_id": "django__django-13710", "file_changes": [{"file": "django/contrib/admin/options.py", "changes": {"edited_entities": ["django/contrib/admin/options.py:InlineModelAdmin.__init__"], "edited_modules": ["django/contrib/admin/options.py:InlineModelAdmin"]}}], "repo": "django/django", "base_commit": "1bd6a7a0acc11e249fca11c017505ad39f15ebf6", "problem_statement": "Use Admin Inline verbose_name as default for Inline verbose_name_plural\nDescription\n\t\nDjango allows specification of a verbose_name and a verbose_name_plural for Inline classes in admin views. However, verbose_name_plural for an Inline is not currently based on a specified verbose_name. Instead, it continues to be based on the model name, or an a verbose_name specified in the model's Meta class. This was confusing to me initially (I didn't understand why I had to specify both name forms for an Inline if I wanted to overrule the default name), and seems inconsistent with the approach for a model's Meta class (which does automatically base the plural form on a specified verbose_name). I propose that verbose_name_plural for an Inline class should by default be based on the verbose_name for an Inline if that is specified.\nI have written a patch to implement this, including tests. Would be happy to submit that.\n", "patch": "diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py\n--- a/django/contrib/admin/options.py\n+++ b/django/contrib/admin/options.py\n@@ -2037,10 +2037,13 @@ def __init__(self, parent_model, admin_site):\n self.opts = self.model._meta\n self.has_registered_model = admin_site.is_registered(self.model)\n super().__init__()\n+ if self.verbose_name_plural is None:\n+ if self.verbose_name is None:\n+ self.verbose_name_plural = self.model._meta.verbose_name_plural\n+ else:\n+ self.verbose_name_plural = format_lazy('{}s', self.verbose_name)\n if self.verbose_name is None:\n self.verbose_name = self.model._meta.verbose_name\n- if self.verbose_name_plural is None:\n- self.verbose_name_plural = self.model._meta.verbose_name_plural\n \n @property\n def media(self):\n"} +{"instance_id": "django__django-13757", "file_changes": [{"file": "django/db/models/fields/json.py", "changes": {"edited_entities": ["django/db/models/fields/json.py:KeyTransformIsNull.as_oracle", "django/db/models/fields/json.py:KeyTransformIsNull.as_sqlite"], "edited_modules": ["django/db/models/fields/json.py:KeyTransformIsNull"]}}], "repo": "django/django", "base_commit": "3f140dde51c0fe6c350acb7727bbe489a99f0632", "problem_statement": "Using __isnull=True on a KeyTransform should not match JSON null on SQLite and Oracle\nDescription\n\t\nThe KeyTransformIsNull lookup borrows the logic from HasKey for isnull=False, which is correct. If isnull=True, the query should only match objects that do not have the key. The query is correct for MariaDB, MySQL, and PostgreSQL. However, on SQLite and Oracle, the query also matches objects that have the key with the value null, which is incorrect.\nTo confirm, edit tests.model_fields.test_jsonfield.TestQuerying.test_isnull_key. For the first assertion, change\n\t\tself.assertSequenceEqual(\n\t\t\tNullableJSONModel.objects.filter(value__a__isnull=True),\n\t\t\tself.objs[:3] + self.objs[5:],\n\t\t)\nto\n\t\tself.assertSequenceEqual(\n\t\t\tNullableJSONModel.objects.filter(value__j__isnull=True),\n\t\t\tself.objs[:4] + self.objs[5:],\n\t\t)\nThe test previously only checks with value__a which could not catch this behavior because the value is not JSON null.\n", "patch": "diff --git a/django/db/models/fields/json.py b/django/db/models/fields/json.py\n--- a/django/db/models/fields/json.py\n+++ b/django/db/models/fields/json.py\n@@ -366,14 +366,25 @@ def process_rhs(self, compiler, connection):\n class KeyTransformIsNull(lookups.IsNull):\n # key__isnull=False is the same as has_key='key'\n def as_oracle(self, compiler, connection):\n+ sql, params = HasKey(\n+ self.lhs.lhs,\n+ self.lhs.key_name,\n+ ).as_oracle(compiler, connection)\n if not self.rhs:\n- return HasKey(self.lhs.lhs, self.lhs.key_name).as_oracle(compiler, connection)\n- return super().as_sql(compiler, connection)\n+ return sql, params\n+ # Column doesn't have a key or IS NULL.\n+ lhs, lhs_params, _ = self.lhs.preprocess_lhs(compiler, connection)\n+ return '(NOT %s OR %s IS NULL)' % (sql, lhs), tuple(params) + tuple(lhs_params)\n \n def as_sqlite(self, compiler, connection):\n+ template = 'JSON_TYPE(%s, %%s) IS NULL'\n if not self.rhs:\n- return HasKey(self.lhs.lhs, self.lhs.key_name).as_sqlite(compiler, connection)\n- return super().as_sql(compiler, connection)\n+ template = 'JSON_TYPE(%s, %%s) IS NOT NULL'\n+ return HasKey(self.lhs.lhs, self.lhs.key_name).as_sql(\n+ compiler,\n+ connection,\n+ template=template,\n+ )\n \n \n class KeyTransformIn(lookups.In):\n"} +{"instance_id": "django__django-13768", "file_changes": [{"file": "django/dispatch/dispatcher.py", "changes": {"edited_entities": ["django/dispatch/dispatcher.py:Signal.send_robust"], "edited_modules": ["django/dispatch/dispatcher.py:Signal"]}}], "repo": "django/django", "base_commit": "965d2d95c630939b53eb60d9c169f5dfc77ee0c6", "problem_statement": "Log exceptions handled in Signal.send_robust()\nDescription\n\t\nAs pointed out by \u200bHaki Benita on Twitter, by default Signal.send_robust() doesn't have any log messages for exceptions raised in receivers. Since Django logs exceptions in other similar situations, such as missing template variables, I think it would be worth adding a logger.exception() call in the except clause of send_robust() . Users would then see such exceptions in their error handling tools, e.g. Sentry, and be able to figure out what action to take from there. Ultimately any *expected* exception should be caught with a try in the receiver function.\n", "patch": "diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py\n--- a/django/dispatch/dispatcher.py\n+++ b/django/dispatch/dispatcher.py\n@@ -1,3 +1,4 @@\n+import logging\n import threading\n import warnings\n import weakref\n@@ -5,6 +6,8 @@\n from django.utils.deprecation import RemovedInDjango40Warning\n from django.utils.inspect import func_accepts_kwargs\n \n+logger = logging.getLogger('django.dispatch')\n+\n \n def _make_id(target):\n if hasattr(target, '__func__'):\n@@ -208,6 +211,12 @@ def send_robust(self, sender, **named):\n try:\n response = receiver(signal=self, sender=sender, **named)\n except Exception as err:\n+ logger.error(\n+ 'Error calling %s in Signal.send_robust() (%s)',\n+ receiver.__qualname__,\n+ err,\n+ exc_info=err,\n+ )\n responses.append((receiver, err))\n else:\n responses.append((receiver, response))\n"} +{"instance_id": "django__django-13925", "file_changes": [{"file": "django/db/models/base.py", "changes": {"edited_entities": ["django/db/models/base.py:Model._check_default_pk"], "edited_modules": ["django/db/models/base.py:Model"]}}], "repo": "django/django", "base_commit": "0c42cdf0d2422f4c080e93594d5d15381d6e955e", "problem_statement": "models.W042 is raised on inherited manually specified primary key.\nDescription\n\t\nI have models which inherit from other models, and they should inherit the primary key. This works fine with Django 3.1. However, if I install Django 3.2 alpha, when I run make_migrations I get the following error messages:\nSystem check identified some issues:\nWARNINGS:\naccounts.ReservedUsername: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the SpeedyCoreAccountsConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\naccounts.User: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the SpeedyCoreAccountsConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nblocks.Block: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\ncontact_by_form.Feedback: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the SpeedyCoreContactByFormConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\ncore_messages.ReadMark: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the SpeedyCoreMessagesConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nfriendship.Block: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nfriendship.Follow: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nfriendship.Friend: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nfriendship.FriendshipRequest: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nlikes.UserLike: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nuploads.Image: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.\n\t\tHINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.\nThese models should not use auto-created primary keys! I already defined the primary key in the ancestor of the model. For example class Entity which class User inherits from. It looks to me like a bug in Django 3.2 alpha.\n", "patch": "diff --git a/django/db/models/base.py b/django/db/models/base.py\n--- a/django/db/models/base.py\n+++ b/django/db/models/base.py\n@@ -1299,6 +1299,11 @@ def check(cls, **kwargs):\n def _check_default_pk(cls):\n if (\n cls._meta.pk.auto_created and\n+ # Inherited PKs are checked in parents models.\n+ not (\n+ isinstance(cls._meta.pk, OneToOneField) and\n+ cls._meta.pk.remote_field.parent_link\n+ ) and\n not settings.is_overridden('DEFAULT_AUTO_FIELD') and\n not cls._meta.app_config._is_default_auto_field_overridden\n ):\n"} +{"instance_id": "django__django-13933", "file_changes": [{"file": "django/forms/models.py", "changes": {"edited_entities": ["django/forms/models.py:ModelChoiceField.to_python"], "edited_modules": ["django/forms/models.py:ModelChoiceField"]}}], "repo": "django/django", "base_commit": "42e8cf47c7ee2db238bf91197ea398126c546741", "problem_statement": "ModelChoiceField does not provide value of invalid choice when raising ValidationError\nDescription\n\t \n\t\t(last modified by Aaron Wiegel)\n\t \nCompared with ChoiceField and others, ModelChoiceField does not show the value of the invalid choice when raising a validation error. Passing in parameters with the invalid value and modifying the default error message for the code invalid_choice should fix this.\nFrom source code:\nclass ModelMultipleChoiceField(ModelChoiceField):\n\t\"\"\"A MultipleChoiceField whose choices are a model QuerySet.\"\"\"\n\twidget = SelectMultiple\n\thidden_widget = MultipleHiddenInput\n\tdefault_error_messages = {\n\t\t'invalid_list': _('Enter a list of values.'),\n\t\t'invalid_choice': _('Select a valid choice. %(value)s is not one of the'\n\t\t\t\t\t\t\t' available choices.'),\n\t\t'invalid_pk_value': _('\u201c%(pk)s\u201d is not a valid value.')\n\t}\n\t...\nclass ModelChoiceField(ChoiceField):\n\t\"\"\"A ChoiceField whose choices are a model QuerySet.\"\"\"\n\t# This class is a subclass of ChoiceField for purity, but it doesn't\n\t# actually use any of ChoiceField's implementation.\n\tdefault_error_messages = {\n\t\t'invalid_choice': _('Select a valid choice. That choice is not one of'\n\t\t\t\t\t\t\t' the available choices.'),\n\t}\n\t...\n", "patch": "diff --git a/django/forms/models.py b/django/forms/models.py\n--- a/django/forms/models.py\n+++ b/django/forms/models.py\n@@ -1284,7 +1284,11 @@ def to_python(self, value):\n value = getattr(value, key)\n value = self.queryset.get(**{key: value})\n except (ValueError, TypeError, self.queryset.model.DoesNotExist):\n- raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')\n+ raise ValidationError(\n+ self.error_messages['invalid_choice'],\n+ code='invalid_choice',\n+ params={'value': value},\n+ )\n return value\n \n def validate(self, value):\n"} +{"instance_id": "django__django-13964", "file_changes": [{"file": "django/db/models/base.py", "changes": {"edited_entities": ["django/db/models/base.py:Model._prepare_related_fields_for_save"], "edited_modules": ["django/db/models/base.py:Model"]}}], "repo": "django/django", "base_commit": "f39634ff229887bf7790c069d0c411b38494ca38", "problem_statement": "Saving parent object after setting on child leads to data loss for parents with non-numeric primary key.\nDescription\n\t \n\t\t(last modified by Charlie DeTar)\n\t \nGiven a model with a foreign key relation to another model that has a non-auto CharField as its primary key:\nclass Product(models.Model):\n\tsku = models.CharField(primary_key=True, max_length=50)\nclass Order(models.Model):\n\tproduct = models.ForeignKey(Product, on_delete=models.CASCADE)\nIf the relation is initialized on the parent with an empty instance that does not yet specify its primary key, and the primary key is subsequently defined, the parent does not \"see\" the primary key's change:\nwith transaction.atomic():\n\torder = Order()\n\torder.product = Product()\n\torder.product.sku = \"foo\"\n\torder.product.save()\n\torder.save()\n\tassert Order.objects.filter(product_id=\"\").exists() # Succeeds, but shouldn't\n\tassert Order.objects.filter(product=order.product).exists() # Fails\nInstead of product_id being populated with product.sku, it is set to emptystring. The foreign key constraint which would enforce the existence of a product with sku=\"\" is deferred until the transaction commits. The transaction does correctly fail on commit with a ForeignKeyViolation due to the non-existence of a product with emptystring as its primary key.\nOn the other hand, if the related unsaved instance is initialized with its primary key before assignment to the parent, it is persisted correctly:\nwith transaction.atomic():\n\torder = Order()\n\torder.product = Product(sku=\"foo\")\n\torder.product.save()\n\torder.save()\n\tassert Order.objects.filter(product=order.product).exists() # succeeds\nCommitting the transaction also succeeds.\nThis may have something to do with how the Order.product_id field is handled at assignment, together with something about handling fetching of auto vs non-auto primary keys from the related instance.\n", "patch": "diff --git a/django/db/models/base.py b/django/db/models/base.py\n--- a/django/db/models/base.py\n+++ b/django/db/models/base.py\n@@ -933,7 +933,7 @@ def _prepare_related_fields_for_save(self, operation_name):\n \"%s() prohibited to prevent data loss due to unsaved \"\n \"related object '%s'.\" % (operation_name, field.name)\n )\n- elif getattr(self, field.attname) is None:\n+ elif getattr(self, field.attname) in field.empty_values:\n # Use pk from related object if it has been saved after\n # an assignment.\n setattr(self, field.attname, obj.pk)\n"} +{"instance_id": "django__django-14016", "file_changes": [{"file": "django/db/models/query_utils.py", "changes": {"edited_entities": ["django/db/models/query_utils.py:Q._combine"], "edited_modules": ["django/db/models/query_utils.py:Q"]}}], "repo": "django/django", "base_commit": "1710cdbe79c90665046034fe1700933d038d90ad", "problem_statement": "\"TypeError: cannot pickle\" when applying | operator to a Q object\nDescription\n\t \n\t\t(last modified by Daniel Izquierdo)\n\t \nUsing a reference to a non-pickleable type of object such as dict_keys in a Q object makes the | operator fail:\n>>> from django.db.models import Q\n>>> Q(x__in={}.keys())\n\n>>> Q() | Q(x__in={}.keys())\nTraceback (most recent call last):\n...\nTypeError: cannot pickle 'dict_keys' object\nEven though this particular example could be solved by doing Q() | Q(x__in={}) it still feels like using .keys() should work.\nI can work on a patch if there's agreement that this should not crash.\n", "patch": "diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py\n--- a/django/db/models/query_utils.py\n+++ b/django/db/models/query_utils.py\n@@ -5,7 +5,6 @@\n large and/or so that they can be used by other modules without getting into\n circular import difficulties.\n \"\"\"\n-import copy\n import functools\n import inspect\n from collections import namedtuple\n@@ -46,10 +45,12 @@ def _combine(self, other, conn):\n \n # If the other Q() is empty, ignore it and just use `self`.\n if not other:\n- return copy.deepcopy(self)\n+ _, args, kwargs = self.deconstruct()\n+ return type(self)(*args, **kwargs)\n # Or if this Q is empty, ignore it and just use `other`.\n elif not self:\n- return copy.deepcopy(other)\n+ _, args, kwargs = other.deconstruct()\n+ return type(other)(*args, **kwargs)\n \n obj = type(self)()\n obj.connector = conn\n"} +{"instance_id": "django__django-14017", "file_changes": [{"file": "django/db/models/query_utils.py", "changes": {"edited_entities": ["django/db/models/query_utils.py:Q._combine"], "edited_modules": ["django/db/models/query_utils.py:Q"]}}], "repo": "django/django", "base_commit": "466920f6d726eee90d5566e0a9948e92b33a122e", "problem_statement": "Q(...) & Exists(...) raises a TypeError\nDescription\n\t\nExists(...) & Q(...) works, but Q(...) & Exists(...) raise a TypeError\nHere's a minimal example:\nIn [3]: Exists(Product.objects.all()) & Q()\nOut[3]: , (AND: ))>\nIn [4]: Q() & Exists(Product.objects.all())\n---------------------------------------------------------------------------\nTypeError\t\t\t\t\t\t\t\t Traceback (most recent call last)\n in \n----> 1 Q() & Exists(Product.objects.all())\n~/Code/venv/ecom/lib/python3.8/site-packages/django/db/models/query_utils.py in __and__(self, other)\n\t 90 \n\t 91\t def __and__(self, other):\n---> 92\t\t return self._combine(other, self.AND)\n\t 93 \n\t 94\t def __invert__(self):\n~/Code/venv/ecom/lib/python3.8/site-packages/django/db/models/query_utils.py in _combine(self, other, conn)\n\t 71\t def _combine(self, other, conn):\n\t 72\t\t if not isinstance(other, Q):\n---> 73\t\t\t raise TypeError(other)\n\t 74 \n\t 75\t\t # If the other Q() is empty, ignore it and just use `self`.\nTypeError: \nThe & (and |) operators should be commutative on Q-Exists pairs, but it's not\nI think there's a missing definition of __rand__ somewhere.\n", "patch": "diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py\n--- a/django/db/models/query_utils.py\n+++ b/django/db/models/query_utils.py\n@@ -40,7 +40,7 @@ def __init__(self, *args, _connector=None, _negated=False, **kwargs):\n super().__init__(children=[*args, *sorted(kwargs.items())], connector=_connector, negated=_negated)\n \n def _combine(self, other, conn):\n- if not isinstance(other, Q):\n+ if not(isinstance(other, Q) or getattr(other, 'conditional', False) is True):\n raise TypeError(other)\n \n # If the other Q() is empty, ignore it and just use `self`.\n"} +{"instance_id": "django__django-14155", "file_changes": [{"file": "django/urls/resolvers.py", "changes": {"edited_entities": ["django/urls/resolvers.py:ResolverMatch.__repr__"], "edited_modules": ["django/urls/resolvers.py:ResolverMatch"]}}], "repo": "django/django", "base_commit": "2f13c476abe4ba787b6cb71131818341911f43cc", "problem_statement": "ResolverMatch.__repr__() doesn't handle functools.partial() nicely.\nDescription\n\t \n\t\t(last modified by Nick Pope)\n\t \nWhen a partial function is passed as the view, the __repr__ shows the func argument as functools.partial which isn't very helpful, especially as it doesn't reveal the underlying function or arguments provided.\nBecause a partial function also has arguments provided up front, we need to handle those specially so that they are accessible in __repr__.\nISTM that we can simply unwrap functools.partial objects in ResolverMatch.__init__().\n", "patch": "diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py\n--- a/django/urls/resolvers.py\n+++ b/django/urls/resolvers.py\n@@ -59,9 +59,16 @@ def __getitem__(self, index):\n return (self.func, self.args, self.kwargs)[index]\n \n def __repr__(self):\n- return \"ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_names=%s, namespaces=%s, route=%s)\" % (\n- self._func_path, self.args, self.kwargs, self.url_name,\n- self.app_names, self.namespaces, self.route,\n+ if isinstance(self.func, functools.partial):\n+ func = repr(self.func)\n+ else:\n+ func = self._func_path\n+ return (\n+ 'ResolverMatch(func=%s, args=%r, kwargs=%r, url_name=%r, '\n+ 'app_names=%r, namespaces=%r, route=%r)' % (\n+ func, self.args, self.kwargs, self.url_name,\n+ self.app_names, self.namespaces, self.route,\n+ )\n )\n \n \n"} +{"instance_id": "django__django-14238", "file_changes": [{"file": "django/db/models/fields/__init__.py", "changes": {"edited_entities": ["django/db/models/fields/__init__.py:AutoFieldMeta.__subclasscheck__"], "edited_modules": ["django/db/models/fields/__init__.py:AutoFieldMeta"]}}], "repo": "django/django", "base_commit": "30e123ed351317b7527f632b3b7dc4e81e850449", "problem_statement": "DEFAULT_AUTO_FIELD subclass check fails for subclasses of BigAutoField and SmallAutoField.\nDescription\n\t\nSet DEFAULT_AUTO_FIELD = \"example.core.models.MyBigAutoField\" , with contents of example.core.models:\nfrom django.db import models\nclass MyBigAutoField(models.BigAutoField):\n\tpass\nclass MyModel(models.Model):\n\tpass\nDjango then crashes with:\nTraceback (most recent call last):\n File \"/..././manage.py\", line 21, in \n\tmain()\n File \"/..././manage.py\", line 17, in main\n\texecute_from_command_line(sys.argv)\n File \"/.../venv/lib/python3.9/site-packages/django/core/management/__init__.py\", line 419, in execute_from_command_line\n\tutility.execute()\n File \"/.../venv/lib/python3.9/site-packages/django/core/management/__init__.py\", line 395, in execute\n\tdjango.setup()\n File \"/.../venv/lib/python3.9/site-packages/django/__init__.py\", line 24, in setup\n\tapps.populate(settings.INSTALLED_APPS)\n File \"/.../venv/lib/python3.9/site-packages/django/apps/registry.py\", line 114, in populate\n\tapp_config.import_models()\n File \"/.../venv/lib/python3.9/site-packages/django/apps/config.py\", line 301, in import_models\n\tself.models_module = import_module(models_module_name)\n File \"/Users/chainz/.pyenv/versions/3.9.1/lib/python3.9/importlib/__init__.py\", line 127, in import_module\n\treturn _bootstrap._gcd_import(name[level:], package, level)\n File \"\", line 1030, in _gcd_import\n File \"\", line 1007, in _find_and_load\n File \"\", line 986, in _find_and_load_unlocked\n File \"\", line 680, in _load_unlocked\n File \"\", line 790, in exec_module\n File \"\", line 228, in _call_with_frames_removed\n File \"/.../example/core/models.py\", line 8, in \n\tclass MyModel(models.Model):\n File \"/.../venv/lib/python3.9/site-packages/django/db/models/base.py\", line 320, in __new__\n\tnew_class._prepare()\n File \"/.../venv/lib/python3.9/site-packages/django/db/models/base.py\", line 333, in _prepare\n\topts._prepare(cls)\n File \"/.../venv/lib/python3.9/site-packages/django/db/models/options.py\", line 285, in _prepare\n\tpk_class = self._get_default_pk_class()\n File \"/.../venv/lib/python3.9/site-packages/django/db/models/options.py\", line 246, in _get_default_pk_class\n\traise ValueError(\nValueError: Primary key 'example.core.models.MyBigAutoField' referred by DEFAULT_AUTO_FIELD must subclass AutoField.\nThis can be fixed in AutoFieldMeta.__subclasscheck__ by allowing subclasses of those classes in the _subclasses property.\n", "patch": "diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py\n--- a/django/db/models/fields/__init__.py\n+++ b/django/db/models/fields/__init__.py\n@@ -2524,7 +2524,7 @@ def __instancecheck__(self, instance):\n return isinstance(instance, self._subclasses) or super().__instancecheck__(instance)\n \n def __subclasscheck__(self, subclass):\n- return subclass in self._subclasses or super().__subclasscheck__(subclass)\n+ return issubclass(subclass, self._subclasses) or super().__subclasscheck__(subclass)\n \n \n class AutoField(AutoFieldMixin, IntegerField, metaclass=AutoFieldMeta):\n"} +{"instance_id": "django__django-14382", "file_changes": [{"file": "django/core/management/templates.py", "changes": {"edited_entities": ["django/core/management/templates.py:TemplateCommand.handle"], "edited_modules": ["django/core/management/templates.py:TemplateCommand"]}}], "repo": "django/django", "base_commit": "29345aecf6e8d53ccb3577a3762bb0c263f7558d", "problem_statement": "django-admin startapp with trailing slash in directory name results in error\nDescription\n\t\nBash tab-completion appends trailing slashes to directory names. django-admin startapp name directory/ results in the error:\nCommandError: '' is not a valid app directory. Please make sure the directory is a valid identifier.\nThe error is caused by \u200bline 77 of django/core/management/templates.py by calling basename() on the path with no consideration for a trailing slash:\nself.validate_name(os.path.basename(target), 'directory')\nRemoving potential trailing slashes would solve the problem:\nself.validate_name(os.path.basename(target.rstrip(os.sep)), 'directory')\n", "patch": "diff --git a/django/core/management/templates.py b/django/core/management/templates.py\n--- a/django/core/management/templates.py\n+++ b/django/core/management/templates.py\n@@ -73,9 +73,9 @@ def handle(self, app_or_project, name, target=None, **options):\n except OSError as e:\n raise CommandError(e)\n else:\n- if app_or_project == 'app':\n- self.validate_name(os.path.basename(target), 'directory')\n top_dir = os.path.abspath(os.path.expanduser(target))\n+ if app_or_project == 'app':\n+ self.validate_name(os.path.basename(top_dir), 'directory')\n if not os.path.exists(top_dir):\n raise CommandError(\"Destination directory '%s' does not \"\n \"exist, please create it first.\" % top_dir)\n"} +{"instance_id": "django__django-14411", "file_changes": [{"file": "django/contrib/auth/forms.py", "changes": {"added_entities": ["django/contrib/auth/forms.py:ReadOnlyPasswordHashWidget.id_for_label"], "added_modules": ["django/contrib/auth/forms.py:ReadOnlyPasswordHashWidget"]}}], "repo": "django/django", "base_commit": "fa4e963ee7e6876581b5432363603571839ba00c", "problem_statement": "Label for ReadOnlyPasswordHashWidget points to non-labelable element.\nDescription\n\t \n\t\t(last modified by David Sanders)\n\t \nIn the admin, the label element for the ReadOnlyPasswordHashWidget widget has a 'for' attribute which points to a non-labelable element, since the widget just renders text, not an input. There's no labelable element for the widget, so the label shouldn't have a 'for' attribute.\n", "patch": "diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py\n--- a/django/contrib/auth/forms.py\n+++ b/django/contrib/auth/forms.py\n@@ -50,6 +50,9 @@ def get_context(self, name, value, attrs):\n context['summary'] = summary\n return context\n \n+ def id_for_label(self, id_):\n+ return None\n+\n \n class ReadOnlyPasswordHashField(forms.Field):\n widget = ReadOnlyPasswordHashWidget\n"} +{"instance_id": "django__django-14534", "file_changes": [{"file": "django/forms/boundfield.py", "changes": {"edited_entities": ["django/forms/boundfield.py:BoundWidget.id_for_label"], "edited_modules": ["django/forms/boundfield.py:BoundWidget"]}}], "repo": "django/django", "base_commit": "910ecd1b8df7678f45c3d507dde6bcb1faafa243", "problem_statement": "BoundWidget.id_for_label ignores id set by ChoiceWidget.options\nDescription\n\t\nIf you look at the implementation of BoundField.subwidgets\nclass BoundField:\n\t...\n\tdef subwidgets(self):\n\t\tid_ = self.field.widget.attrs.get('id') or self.auto_id\n\t\tattrs = {'id': id_} if id_ else {}\n\t\tattrs = self.build_widget_attrs(attrs)\n\t\treturn [\n\t\t\tBoundWidget(self.field.widget, widget, self.form.renderer)\n\t\t\tfor widget in self.field.widget.subwidgets(self.html_name, self.value(), attrs=attrs)\n\t\t]\none sees that self.field.widget.subwidgets(self.html_name, self.value(), attrs=attrs) returns a dict and assigns it to widget. Now widget['attrs']['id'] contains the \"id\" we would like to use when rendering the label of our CheckboxSelectMultiple.\nHowever BoundWidget.id_for_label() is implemented as\nclass BoundWidget:\n\t...\n\tdef id_for_label(self):\n\t\treturn 'id_%s_%s' % (self.data['name'], self.data['index'])\nignoring the id available through self.data['attrs']['id']. This re-implementation for rendering the \"id\" is confusing and presumably not intended. Nobody has probably realized that so far, because rarely the auto_id-argument is overridden when initializing a form. If however we do, one would assume that the method BoundWidget.id_for_label renders that string as specified through the auto_id format-string.\nBy changing the code from above to\nclass BoundWidget:\n\t...\n\tdef id_for_label(self):\n\t\treturn self.data['attrs']['id']\nthat function behaves as expected.\nPlease note that this error only occurs when rendering the subwidgets of a widget of type CheckboxSelectMultiple. This has nothing to do with the method BoundField.id_for_label().\n", "patch": "diff --git a/django/forms/boundfield.py b/django/forms/boundfield.py\n--- a/django/forms/boundfield.py\n+++ b/django/forms/boundfield.py\n@@ -277,7 +277,7 @@ def template_name(self):\n \n @property\n def id_for_label(self):\n- return 'id_%s_%s' % (self.data['name'], self.data['index'])\n+ return self.data['attrs'].get('id')\n \n @property\n def choice_label(self):\n"} +{"instance_id": "django__django-14580", "file_changes": [{"file": "django/db/migrations/serializer.py", "changes": {"edited_entities": ["django/db/migrations/serializer.py:TypeSerializer.serialize"], "edited_modules": ["django/db/migrations/serializer.py:TypeSerializer"]}}], "repo": "django/django", "base_commit": "36fa071d6ebd18a61c4d7f1b5c9d17106134bd44", "problem_statement": "Missing import statement in generated migration (NameError: name 'models' is not defined)\nDescription\n\t\nI found a bug in Django's latest release: 3.2.4. \nGiven the following contents of models.py:\nfrom django.db import models\nclass MyField(models.TextField):\n\tpass\nclass MyBaseModel(models.Model):\n\tclass Meta:\n\t\tabstract = True\nclass MyMixin:\n\tpass\nclass MyModel(MyMixin, MyBaseModel):\n\tname = MyField(primary_key=True)\nThe makemigrations command will generate the following migration file:\n# Generated by Django 3.2.4 on 2021-06-30 19:13\nimport app.models\nfrom django.db import migrations\nclass Migration(migrations.Migration):\n\tinitial = True\n\tdependencies = [\n\t]\n\toperations = [\n\t\tmigrations.CreateModel(\n\t\t\tname='MyModel',\n\t\t\tfields=[\n\t\t\t\t('name', app.models.MyField(primary_key=True, serialize=False)),\n\t\t\t],\n\t\t\toptions={\n\t\t\t\t'abstract': False,\n\t\t\t},\n\t\t\tbases=(app.models.MyMixin, models.Model),\n\t\t),\n\t]\nWhich will then fail with the following error:\n File \"/home/jj/django_example/app/migrations/0001_initial.py\", line 7, in \n\tclass Migration(migrations.Migration):\n File \"/home/jj/django_example/app/migrations/0001_initial.py\", line 23, in Migration\n\tbases=(app.models.MyMixin, models.Model),\nNameError: name 'models' is not defined\nExpected behavior: Django generates a migration file that is valid Python.\nActual behavior: Django generates a migration file that is missing an import statement.\nI think this is a bug of the module django.db.migrations.writer, but I'm not sure. I will be happy to assist with debugging.\nThanks for your attention,\nJaap Joris\n", "patch": "diff --git a/django/db/migrations/serializer.py b/django/db/migrations/serializer.py\n--- a/django/db/migrations/serializer.py\n+++ b/django/db/migrations/serializer.py\n@@ -273,7 +273,7 @@ def _format(self):\n class TypeSerializer(BaseSerializer):\n def serialize(self):\n special_cases = [\n- (models.Model, \"models.Model\", []),\n+ (models.Model, \"models.Model\", ['from django.db import models']),\n (type(None), 'type(None)', []),\n ]\n for case, string, imports in special_cases:\n"} +{"instance_id": "django__django-14608", "file_changes": [{"file": "django/forms/formsets.py", "changes": {"edited_entities": ["django/forms/formsets.py:BaseFormSet.full_clean"], "edited_modules": ["django/forms/formsets.py:BaseFormSet"]}}], "repo": "django/django", "base_commit": "7f33c1e22dbc34a7afae7967783725b10f1f13b1", "problem_statement": "Add `nonform` CSS class for non form errors in FormSets\nDescription\n\t \n\t\t(last modified by Ties Jan Hefting)\n\t \nForms add the nonfield CSS class for non field errors in ErrorList instances. This is documented in a section on \u200brendering form error messages. Similarly, in FormSets I'd expect to see the nonform CSS class added for non form errors. This would allow a custom ErrorList to make a distinction in form field errors, non field errors (forms) and non form errors (FormSets) when rendering error messages. Therefore I'd suggest to add this nonform CSS class and document it for developers to use.\n", "patch": "diff --git a/django/forms/formsets.py b/django/forms/formsets.py\n--- a/django/forms/formsets.py\n+++ b/django/forms/formsets.py\n@@ -333,7 +333,7 @@ def full_clean(self):\n self._non_form_errors.\n \"\"\"\n self._errors = []\n- self._non_form_errors = self.error_class()\n+ self._non_form_errors = self.error_class(error_class='nonform')\n empty_forms_count = 0\n \n if not self.is_bound: # Stop further processing.\n@@ -380,7 +380,10 @@ def full_clean(self):\n # Give self.clean() a chance to do cross-form validation.\n self.clean()\n except ValidationError as e:\n- self._non_form_errors = self.error_class(e.error_list)\n+ self._non_form_errors = self.error_class(\n+ e.error_list,\n+ error_class='nonform'\n+ )\n \n def clean(self):\n \"\"\"\n"} +{"instance_id": "django__django-14667", "file_changes": [{"file": "django/db/models/sql/query.py", "changes": {"edited_entities": ["django/db/models/sql/query.py:Query.add_deferred_loading"], "edited_modules": ["django/db/models/sql/query.py:Query"]}}], "repo": "django/django", "base_commit": "6a970a8b4600eb91be25f38caed0a52269d6303d", "problem_statement": "QuerySet.defer() doesn't clear deferred field when chaining with only().\nDescription\n\t\nConsidering a simple Company model with four fields: id, name, trade_number and country. If we evaluate a queryset containing a .defer() following a .only(), the generated sql query selects unexpected fields. For example: \nCompany.objects.only(\"name\").defer(\"name\")\nloads all the fields with the following query:\nSELECT \"company\".\"id\", \"company\".\"name\", \"company\".\"trade_number\", \"company\".\"country\" FROM \"company\"\nand \nCompany.objects.only(\"name\").defer(\"name\").defer(\"country\")\nalso loads all the fields with the same query:\nSELECT \"company\".\"id\", \"company\".\"name\", \"company\".\"trade_number\", \"company\".\"country\" FROM \"company\"\nIn those two cases, i would expect the sql query to be:\nSELECT \"company\".\"id\" FROM \"company\"\nIn the following example, we get the expected behavior:\nCompany.objects.only(\"name\", \"country\").defer(\"name\")\nonly loads \"id\" and \"country\" fields with the following query:\nSELECT \"company\".\"id\", \"company\".\"country\" FROM \"company\"\n", "patch": "diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py\n--- a/django/db/models/sql/query.py\n+++ b/django/db/models/sql/query.py\n@@ -2086,7 +2086,12 @@ def add_deferred_loading(self, field_names):\n self.deferred_loading = existing.union(field_names), True\n else:\n # Remove names from the set of any existing \"immediate load\" names.\n- self.deferred_loading = existing.difference(field_names), False\n+ if new_existing := existing.difference(field_names):\n+ self.deferred_loading = new_existing, False\n+ else:\n+ self.clear_deferred_loading()\n+ if new_only := set(field_names).difference(existing):\n+ self.deferred_loading = new_only, True\n \n def add_immediate_loading(self, field_names):\n \"\"\"\n"} +{"instance_id": "django__django-14672", "file_changes": [{"file": "django/db/models/fields/reverse_related.py", "changes": {"edited_entities": ["django/db/models/fields/reverse_related.py:ManyToManyRel.identity"], "edited_modules": ["django/db/models/fields/reverse_related.py:ManyToManyRel"]}}], "repo": "django/django", "base_commit": "00ea883ef56fb5e092cbe4a6f7ff2e7470886ac4", "problem_statement": "Missing call `make_hashable` on `through_fields` in `ManyToManyRel`\nDescription\n\t\nIn 3.2 identity property has been added to all ForeignObjectRel to make it possible to compare them. A hash is derived from said identity and it's possible because identity is a tuple. To make limit_choices_to hashable (one of this tuple elements), \u200bthere's a call to make_hashable.\nIt happens that through_fields can be a list. In such case, this make_hashable call is missing in \u200bManyToManyRel.\nFor some reason it only fails on checking proxy model. I think proxy models have 29 checks and normal ones 24, hence the issue, but that's just a guess.\nMinimal repro:\nclass Parent(models.Model):\n\tname = models.CharField(max_length=256)\nclass ProxyParent(Parent):\n\tclass Meta:\n\t\tproxy = True\nclass Child(models.Model):\n\tparent = models.ForeignKey(Parent, on_delete=models.CASCADE)\n\tmany_to_many_field = models.ManyToManyField(\n\t\tto=Parent,\n\t\tthrough=\"ManyToManyModel\",\n\t\tthrough_fields=['child', 'parent'],\n\t\trelated_name=\"something\"\n\t)\nclass ManyToManyModel(models.Model):\n\tparent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='+')\n\tchild = models.ForeignKey(Child, on_delete=models.CASCADE, related_name='+')\n\tsecond_child = models.ForeignKey(Child, on_delete=models.CASCADE, null=True, default=None)\nWhich will result in \n File \"manage.py\", line 23, in \n\tmain()\n File \"manage.py\", line 19, in main\n\texecute_from_command_line(sys.argv)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/__init__.py\", line 419, in execute_from_command_line\n\tutility.execute()\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/__init__.py\", line 413, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/base.py\", line 354, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/base.py\", line 393, in execute\n\tself.check()\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/base.py\", line 419, in check\n\tall_issues = checks.run_checks(\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/checks/registry.py\", line 76, in run_checks\n\tnew_errors = check(app_configs=app_configs, databases=databases)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/checks/model_checks.py\", line 34, in check_all_models\n\terrors.extend(model.check(**kwargs))\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/db/models/base.py\", line 1277, in check\n\t*cls._check_field_name_clashes(),\n File \"/home/tom/PycharmProjects/djangbroken_m2m_projectProject/venv/lib/python3.8/site-packages/django/db/models/base.py\", line 1465, in _check_field_name_clashes\n\tif f not in used_fields:\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/db/models/fields/reverse_related.py\", line 140, in __hash__\n\treturn hash(self.identity)\nTypeError: unhashable type: 'list'\nSolution: Add missing make_hashable call on self.through_fields in ManyToManyRel.\nMissing call `make_hashable` on `through_fields` in `ManyToManyRel`\nDescription\n\t\nIn 3.2 identity property has been added to all ForeignObjectRel to make it possible to compare them. A hash is derived from said identity and it's possible because identity is a tuple. To make limit_choices_to hashable (one of this tuple elements), \u200bthere's a call to make_hashable.\nIt happens that through_fields can be a list. In such case, this make_hashable call is missing in \u200bManyToManyRel.\nFor some reason it only fails on checking proxy model. I think proxy models have 29 checks and normal ones 24, hence the issue, but that's just a guess.\nMinimal repro:\nclass Parent(models.Model):\n\tname = models.CharField(max_length=256)\nclass ProxyParent(Parent):\n\tclass Meta:\n\t\tproxy = True\nclass Child(models.Model):\n\tparent = models.ForeignKey(Parent, on_delete=models.CASCADE)\n\tmany_to_many_field = models.ManyToManyField(\n\t\tto=Parent,\n\t\tthrough=\"ManyToManyModel\",\n\t\tthrough_fields=['child', 'parent'],\n\t\trelated_name=\"something\"\n\t)\nclass ManyToManyModel(models.Model):\n\tparent = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='+')\n\tchild = models.ForeignKey(Child, on_delete=models.CASCADE, related_name='+')\n\tsecond_child = models.ForeignKey(Child, on_delete=models.CASCADE, null=True, default=None)\nWhich will result in \n File \"manage.py\", line 23, in \n\tmain()\n File \"manage.py\", line 19, in main\n\texecute_from_command_line(sys.argv)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/__init__.py\", line 419, in execute_from_command_line\n\tutility.execute()\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/__init__.py\", line 413, in execute\n\tself.fetch_command(subcommand).run_from_argv(self.argv)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/base.py\", line 354, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/base.py\", line 393, in execute\n\tself.check()\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/management/base.py\", line 419, in check\n\tall_issues = checks.run_checks(\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/checks/registry.py\", line 76, in run_checks\n\tnew_errors = check(app_configs=app_configs, databases=databases)\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/core/checks/model_checks.py\", line 34, in check_all_models\n\terrors.extend(model.check(**kwargs))\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/db/models/base.py\", line 1277, in check\n\t*cls._check_field_name_clashes(),\n File \"/home/tom/PycharmProjects/djangbroken_m2m_projectProject/venv/lib/python3.8/site-packages/django/db/models/base.py\", line 1465, in _check_field_name_clashes\n\tif f not in used_fields:\n File \"/home/tom/PycharmProjects/broken_m2m_project/venv/lib/python3.8/site-packages/django/db/models/fields/reverse_related.py\", line 140, in __hash__\n\treturn hash(self.identity)\nTypeError: unhashable type: 'list'\nSolution: Add missing make_hashable call on self.through_fields in ManyToManyRel.\n", "patch": "diff --git a/django/db/models/fields/reverse_related.py b/django/db/models/fields/reverse_related.py\n--- a/django/db/models/fields/reverse_related.py\n+++ b/django/db/models/fields/reverse_related.py\n@@ -310,7 +310,7 @@ def __init__(self, field, to, related_name=None, related_query_name=None,\n def identity(self):\n return super().identity + (\n self.through,\n- self.through_fields,\n+ make_hashable(self.through_fields),\n self.db_constraint,\n )\n \n"} +{"instance_id": "django__django-14730", "file_changes": [{"file": "django/db/models/fields/related.py", "changes": {"edited_entities": ["django/db/models/fields/related.py:ManyToManyField._check_ignored_options"], "edited_modules": ["django/db/models/fields/related.py:ManyToManyField"]}}], "repo": "django/django", "base_commit": "4fe3774c729f3fd5105b3001fe69a70bdca95ac3", "problem_statement": "Prevent developers from defining a related_name on symmetrical ManyToManyFields\nDescription\n\t\nIn ManyToManyField, if the symmetrical argument is passed, or if it's a self-referential ManyToMany relationship, the related field on the target model is not created. However, if a developer passes in the related_name not understanding this fact, they may be confused until they find the information about symmetrical relationship. Thus, it is proposed to raise an error when the user defines a ManyToManyField in this condition.\n", "patch": "diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py\n--- a/django/db/models/fields/related.py\n+++ b/django/db/models/fields/related.py\n@@ -1258,6 +1258,16 @@ def _check_ignored_options(self, **kwargs):\n )\n )\n \n+ if self.remote_field.symmetrical and self._related_name:\n+ warnings.append(\n+ checks.Warning(\n+ 'related_name has no effect on ManyToManyField '\n+ 'with a symmetrical relationship, e.g. to \"self\".',\n+ obj=self,\n+ id='fields.W345',\n+ )\n+ )\n+\n return warnings\n \n def _check_relationship_model(self, from_model=None, **kwargs):\n"} +{"instance_id": "django__django-14752", "file_changes": [{"file": "django/contrib/admin/views/autocomplete.py", "changes": {"edited_entities": ["django/contrib/admin/views/autocomplete.py:AutocompleteJsonView.get"], "edited_modules": ["django/contrib/admin/views/autocomplete.py:AutocompleteJsonView"], "added_entities": ["django/contrib/admin/views/autocomplete.py:AutocompleteJsonView.serialize_result"], "added_modules": ["django/contrib/admin/views/autocomplete.py:AutocompleteJsonView"]}}], "repo": "django/django", "base_commit": "b64db05b9cedd96905d637a2d824cbbf428e40e7", "problem_statement": "Refactor AutocompleteJsonView to support extra fields in autocomplete response\nDescription\n\t \n\t\t(last modified by mrts)\n\t \nAdding data attributes to items in ordinary non-autocomplete foreign key fields that use forms.widgets.Select-based widgets is relatively easy. This enables powerful and dynamic admin site customizations where fields from related models are updated immediately when users change the selected item.\nHowever, adding new attributes to autocomplete field results currently requires extending contrib.admin.views.autocomplete.AutocompleteJsonView and fully overriding the AutocompleteJsonView.get() method. Here's an example:\nclass MyModelAdmin(admin.ModelAdmin):\n\tdef get_urls(self):\n\t\treturn [\n\t\t\tpath('autocomplete/', CustomAutocompleteJsonView.as_view(admin_site=self.admin_site))\n\t\t\tif url.pattern.match('autocomplete/')\n\t\t\telse url for url in super().get_urls()\n\t\t]\nclass CustomAutocompleteJsonView(AutocompleteJsonView):\n\tdef get(self, request, *args, **kwargs):\n\t\tself.term, self.model_admin, self.source_field, to_field_name = self.process_request(request)\n\t\tif not self.has_perm(request):\n\t\t\traise PermissionDenied\n\t\tself.object_list = self.get_queryset()\n\t\tcontext = self.get_context_data()\n\t\treturn JsonResponse({\n\t\t\t'results': [\n\t\t\t\t{'id': str(getattr(obj, to_field_name)), 'text': str(obj), 'notes': obj.notes} # <-- customization here\n\t\t\t\tfor obj in context['object_list']\n\t\t\t],\n\t\t\t'pagination': {'more': context['page_obj'].has_next()},\n\t\t})\nThe problem with this is that as AutocompleteJsonView.get() keeps evolving, there's quite a lot of maintenance overhead required to catch up.\nThe solutions is simple, side-effect- and risk-free: adding a result customization extension point to get() by moving the lines that construct the results inside JsonResponse constructor to a separate method. So instead of\n\t\treturn JsonResponse({\n\t\t\t'results': [\n\t\t\t\t{'id': str(getattr(obj, to_field_name)), 'text': str(obj)}\n\t\t\t\tfor obj in context['object_list']\n\t\t\t],\n\t\t\t'pagination': {'more': context['page_obj'].has_next()},\n\t\t})\nthere would be\n\t\treturn JsonResponse({\n\t\t\t'results': [\n\t\t\t\tself.serialize_result(obj, to_field_name) for obj in context['object_list']\n\t\t\t],\n\t\t\t'pagination': {'more': context['page_obj'].has_next()},\n\t\t})\nwhere serialize_result() contains the original object to dictionary conversion code that would be now easy to override:\ndef serialize_result(self, obj, to_field_name):\n\treturn {'id': str(getattr(obj, to_field_name)), 'text': str(obj)}\nThe example CustomAutocompleteJsonView from above would now become succinct and maintainable:\nclass CustomAutocompleteJsonView(AutocompleteJsonView):\n\tdef serialize_result(self, obj, to_field_name):\n\t\treturn super.serialize_result(obj, to_field_name) | {'notes': obj.notes}\nWhat do you think, is this acceptable? I'm more than happy to provide the patch.\n", "patch": "diff --git a/django/contrib/admin/views/autocomplete.py b/django/contrib/admin/views/autocomplete.py\n--- a/django/contrib/admin/views/autocomplete.py\n+++ b/django/contrib/admin/views/autocomplete.py\n@@ -11,7 +11,8 @@ class AutocompleteJsonView(BaseListView):\n \n def get(self, request, *args, **kwargs):\n \"\"\"\n- Return a JsonResponse with search results of the form:\n+ Return a JsonResponse with search results as defined in\n+ serialize_result(), by default:\n {\n results: [{id: \"123\" text: \"foo\"}],\n pagination: {more: true}\n@@ -26,12 +27,19 @@ def get(self, request, *args, **kwargs):\n context = self.get_context_data()\n return JsonResponse({\n 'results': [\n- {'id': str(getattr(obj, to_field_name)), 'text': str(obj)}\n+ self.serialize_result(obj, to_field_name)\n for obj in context['object_list']\n ],\n 'pagination': {'more': context['page_obj'].has_next()},\n })\n \n+ def serialize_result(self, obj, to_field_name):\n+ \"\"\"\n+ Convert the provided model object to a dictionary that is added to the\n+ results list.\n+ \"\"\"\n+ return {'id': str(getattr(obj, to_field_name)), 'text': str(obj)}\n+\n def get_paginator(self, *args, **kwargs):\n \"\"\"Use the ModelAdmin's paginator.\"\"\"\n return self.model_admin.get_paginator(self.request, *args, **kwargs)\n"} +{"instance_id": "django__django-14787", "file_changes": [{"file": "django/utils/decorators.py", "changes": {"edited_entities": ["django/utils/decorators.py:_multi_decorate"], "edited_modules": ["django/utils/decorators.py:_multi_decorate"]}}], "repo": "django/django", "base_commit": "004b4620f6f4ad87261e149898940f2dcd5757ef", "problem_statement": "method_decorator() should preserve wrapper assignments\nDescription\n\t\nthe function that is passed to the decorator is a partial object and does not have any of the attributes expected from a function i.e. __name__, __module__ etc...\nconsider the following case\ndef logger(func):\n\t@wraps(func)\n\tdef inner(*args, **kwargs):\n\t\ttry:\n\t\t\tresult = func(*args, **kwargs)\n\t\texcept Exception as e:\n\t\t\tresult = str(e)\n\t\tfinally:\n\t\t\tlogger.debug(f\"{func.__name__} called with args: {args} and kwargs: {kwargs} resulting: {result}\")\n\treturn inner\nclass Test:\n\t@method_decorator(logger)\n\tdef hello_world(self):\n\t\treturn \"hello\"\nTest().test_method()\nThis results in the following exception\nAttributeError: 'functools.partial' object has no attribute '__name__'\n", "patch": "diff --git a/django/utils/decorators.py b/django/utils/decorators.py\n--- a/django/utils/decorators.py\n+++ b/django/utils/decorators.py\n@@ -37,7 +37,7 @@ def _wrapper(self, *args, **kwargs):\n # 'self' argument, but it's a closure over self so it can call\n # 'func'. Also, wrap method.__get__() in a function because new\n # attributes can't be set on bound method objects, only on functions.\n- bound_method = partial(method.__get__(self, type(self)))\n+ bound_method = wraps(method)(partial(method.__get__(self, type(self))))\n for dec in decorators:\n bound_method = dec(bound_method)\n return bound_method(*args, **kwargs)\n"} +{"instance_id": "django__django-14855", "file_changes": [{"file": "django/contrib/admin/helpers.py", "changes": {"edited_entities": ["django/contrib/admin/helpers.py:AdminReadonlyField.get_admin_url"], "edited_modules": ["django/contrib/admin/helpers.py:AdminReadonlyField"]}}], "repo": "django/django", "base_commit": "475cffd1d64c690cdad16ede4d5e81985738ceb4", "problem_statement": "Wrong URL generated by get_admin_url for readonly field in custom Admin Site\nDescription\n\t\nWhen a model containing a ForeignKey field is viewed (or edited) in a custom Admin Site, and that ForeignKey field is listed in readonly_fields, the url generated for the link is /admin/... instead of /custom-admin/....\nThis appears to be caused by the following line in django.contrib.admin.helpers get_admin_url:\nurl = reverse(url_name, args=[quote(remote_obj.pk)])\nOther parts of the admin use the current_app keyword parameter to identify the correct current name of the Admin Site. (See django.contrib.admin.options.ModelAdmin response_add as just one example)\nI have been able to correct this specific issue by replacing the above line with:\nurl = reverse(\n\turl_name,\n\targs=[quote(remote_obj.pk)],\n\tcurrent_app=self.model_admin.admin_site.name\n)\nHowever, I don't know if there are any side effects and I have not yet run the full suite of tests on this. Mostly looking for feedback whether I'm on the right track.\n", "patch": "diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py\n--- a/django/contrib/admin/helpers.py\n+++ b/django/contrib/admin/helpers.py\n@@ -209,7 +209,11 @@ def get_admin_url(self, remote_field, remote_obj):\n remote_field.model._meta.model_name,\n )\n try:\n- url = reverse(url_name, args=[quote(remote_obj.pk)])\n+ url = reverse(\n+ url_name,\n+ args=[quote(remote_obj.pk)],\n+ current_app=self.model_admin.admin_site.name,\n+ )\n return format_html('{}', url, remote_obj)\n except NoReverseMatch:\n return str(remote_obj)\n"} +{"instance_id": "django__django-14915", "file_changes": [{"file": "django/forms/models.py", "changes": {"added_entities": ["django/forms/models.py:ModelChoiceIteratorValue.__hash__"], "added_modules": ["django/forms/models.py:ModelChoiceIteratorValue"]}}], "repo": "django/django", "base_commit": "903aaa35e5ceaa33bfc9b19b7f6da65ce5a91dd4", "problem_statement": "ModelChoiceIteratorValue is not hashable.\nDescription\n\t\nRecently I migrated from Django 3.0 to Django 3.1. In my code, I add custom data-* attributes to the select widget options. After the upgrade some of those options broke. Error is {TypeError}unhashable type: 'ModelChoiceIteratorValue'.\nExample (this one breaks):\n\tdef create_option(self, name, value, label, selected, index, subindex=None, attrs=None):\n\t\tcontext = super().create_option(name, value, label, selected, index, subindex, attrs)\n\t\tif not value:\n\t\t\treturn context\n\t\tif value in self.show_fields: # This is a dict {1: ['first_name', 'last_name']}\n\t\t\tcontext['attrs']['data-fields'] = json.dumps(self.show_fields[value])\nHowever, working with arrays is not an issue:\n\tdef create_option(self, name, value, label, selected, index, subindex=None, attrs=None):\n\t\tcontext = super().create_option(name, value, label, selected, index, subindex, attrs)\n\t\tif not value:\n\t\t\treturn context\n\t\tif value in allowed_values: # This is an array [1, 2]\n\t\t\t...\n", "patch": "diff --git a/django/forms/models.py b/django/forms/models.py\n--- a/django/forms/models.py\n+++ b/django/forms/models.py\n@@ -1166,6 +1166,9 @@ def __init__(self, value, instance):\n def __str__(self):\n return str(self.value)\n \n+ def __hash__(self):\n+ return hash(self.value)\n+\n def __eq__(self, other):\n if isinstance(other, ModelChoiceIteratorValue):\n other = other.value\n"} +{"instance_id": "django__django-14997", "file_changes": [{"file": "django/db/backends/ddl_references.py", "changes": {"edited_entities": ["django/db/backends/ddl_references.py:Expressions.rename_table_references"], "edited_modules": ["django/db/backends/ddl_references.py:Expressions"]}}], "repo": "django/django", "base_commit": "0d4e575c96d408e0efb4dfd0cbfc864219776950", "problem_statement": "Remaking table with unique constraint crashes on SQLite.\nDescription\n\t\nIn Django 4.0a1, this model:\nclass Tag(models.Model):\n\tname = models.SlugField(help_text=\"The tag key.\")\n\tvalue = models.CharField(max_length=150, help_text=\"The tag value.\")\n\tclass Meta:\n\t\tordering = [\"name\", \"value\"]\n\t\tconstraints = [\n\t\t\tmodels.UniqueConstraint(\n\t\t\t\t\"name\",\n\t\t\t\t\"value\",\n\t\t\t\tname=\"unique_name_value\",\n\t\t\t)\n\t\t]\n\tdef __str__(self):\n\t\treturn f\"{self.name}={self.value}\"\nwith these migrations, using sqlite:\nclass Migration(migrations.Migration):\n\tinitial = True\n\tdependencies = [\n\t]\n\toperations = [\n\t\tmigrations.CreateModel(\n\t\t\tname='Tag',\n\t\t\tfields=[\n\t\t\t\t('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),\n\t\t\t\t('name', models.SlugField(help_text='The tag key.')),\n\t\t\t\t('value', models.CharField(help_text='The tag value.', max_length=200)),\n\t\t\t],\n\t\t\toptions={\n\t\t\t\t'ordering': ['name', 'value'],\n\t\t\t},\n\t\t),\n\t\tmigrations.AddConstraint(\n\t\t\tmodel_name='tag',\n\t\t\tconstraint=models.UniqueConstraint(django.db.models.expressions.F('name'), django.db.models.expressions.F('value'), name='unique_name_value'),\n\t\t),\n\t]\nclass Migration(migrations.Migration):\n\tdependencies = [\n\t\t('myapp', '0001_initial'),\n\t]\n\toperations = [\n\t\tmigrations.AlterField(\n\t\t\tmodel_name='tag',\n\t\t\tname='value',\n\t\t\tfield=models.CharField(help_text='The tag value.', max_length=150),\n\t\t),\n\t]\nraises this error:\nmanage.py migrate\nOperations to perform:\n Apply all migrations: admin, auth, contenttypes, myapp, sessions\nRunning migrations:\n Applying myapp.0002_alter_tag_value...python-BaseException\nTraceback (most recent call last):\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\sqlite3\\base.py\", line 416, in execute\n\treturn Database.Cursor.execute(self, query, params)\nsqlite3.OperationalError: the \".\" operator prohibited in index expressions\nThe above exception was the direct cause of the following exception:\nTraceback (most recent call last):\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\core\\management\\base.py\", line 373, in run_from_argv\n\tself.execute(*args, **cmd_options)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\core\\management\\base.py\", line 417, in execute\n\toutput = self.handle(*args, **options)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\core\\management\\base.py\", line 90, in wrapped\n\tres = handle_func(*args, **kwargs)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\core\\management\\commands\\migrate.py\", line 253, in handle\n\tpost_migrate_state = executor.migrate(\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\migrations\\executor.py\", line 126, in migrate\n\tstate = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\migrations\\executor.py\", line 156, in _migrate_all_forwards\n\tstate = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\migrations\\executor.py\", line 236, in apply_migration\n\tstate = migration.apply(state, schema_editor)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\migrations\\migration.py\", line 125, in apply\n\toperation.database_forwards(self.app_label, schema_editor, old_state, project_state)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\migrations\\operations\\fields.py\", line 225, in database_forwards\n\tschema_editor.alter_field(from_model, from_field, to_field)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\sqlite3\\schema.py\", line 140, in alter_field\n\tsuper().alter_field(model, old_field, new_field, strict=strict)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\base\\schema.py\", line 618, in alter_field\n\tself._alter_field(model, old_field, new_field, old_type, new_type,\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\sqlite3\\schema.py\", line 362, in _alter_field\n\tself._remake_table(model, alter_field=(old_field, new_field))\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\sqlite3\\schema.py\", line 303, in _remake_table\n\tself.execute(sql)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\base\\schema.py\", line 151, in execute\n\tcursor.execute(sql, params)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 98, in execute\n\treturn super().execute(sql, params)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 66, in execute\n\treturn self._execute_with_wrappers(sql, params, many=False, executor=self._execute)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 75, in _execute_with_wrappers\n\treturn executor(sql, params, many, context)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\utils.py\", line 90, in __exit__\n\traise dj_exc_value.with_traceback(traceback) from exc_value\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\utils.py\", line 84, in _execute\n\treturn self.cursor.execute(sql, params)\n File \"D:\\Projects\\Development\\sqliteerror\\.venv\\lib\\site-packages\\django\\db\\backends\\sqlite3\\base.py\", line 416, in execute\n\treturn Database.Cursor.execute(self, query, params)\ndjango.db.utils.OperationalError: the \".\" operator prohibited in index expressions\n", "patch": "diff --git a/django/db/backends/ddl_references.py b/django/db/backends/ddl_references.py\n--- a/django/db/backends/ddl_references.py\n+++ b/django/db/backends/ddl_references.py\n@@ -212,11 +212,7 @@ def __init__(self, table, expressions, compiler, quote_value):\n def rename_table_references(self, old_table, new_table):\n if self.table != old_table:\n return\n- expressions = deepcopy(self.expressions)\n- self.columns = []\n- for col in self.compiler.query._gen_cols([expressions]):\n- col.alias = new_table\n- self.expressions = expressions\n+ self.expressions = self.expressions.relabeled_clone({old_table: new_table})\n super().rename_table_references(old_table, new_table)\n \n def rename_column_references(self, table, old_column, new_column):\n"} +{"instance_id": "django__django-14999", "file_changes": [{"file": "django/db/migrations/operations/models.py", "changes": {"edited_entities": ["django/db/migrations/operations/models.py:RenameModel.database_forwards"], "edited_modules": ["django/db/migrations/operations/models.py:RenameModel"]}}], "repo": "django/django", "base_commit": "a754b82dac511475b6276039471ccd17cc64aeb8", "problem_statement": "RenameModel with db_table should be a noop.\nDescription\n\t\nA RenameModel operation that already has db_table defined must be a noop.\nIn Postgres, it drops and recreates foreign key constraints. In sqlite it recreates the table (as expected for a table renaming).\n", "patch": "diff --git a/django/db/migrations/operations/models.py b/django/db/migrations/operations/models.py\n--- a/django/db/migrations/operations/models.py\n+++ b/django/db/migrations/operations/models.py\n@@ -320,12 +320,13 @@ def database_forwards(self, app_label, schema_editor, from_state, to_state):\n new_model = to_state.apps.get_model(app_label, self.new_name)\n if self.allow_migrate_model(schema_editor.connection.alias, new_model):\n old_model = from_state.apps.get_model(app_label, self.old_name)\n+ old_db_table = old_model._meta.db_table\n+ new_db_table = new_model._meta.db_table\n+ # Don't alter when a table name is not changed.\n+ if old_db_table == new_db_table:\n+ return\n # Move the main table\n- schema_editor.alter_db_table(\n- new_model,\n- old_model._meta.db_table,\n- new_model._meta.db_table,\n- )\n+ schema_editor.alter_db_table(new_model, old_db_table, new_db_table)\n # Alter the fields pointing to us\n for related_object in old_model._meta.related_objects:\n if related_object.related_model == old_model:\n"} +{"instance_id": "django__django-15061", "file_changes": [{"file": "django/forms/widgets.py", "changes": {"edited_entities": ["django/forms/widgets.py:MultiWidget.id_for_label"], "edited_modules": ["django/forms/widgets.py:MultiWidget"]}}], "repo": "django/django", "base_commit": "2c01ebb4be5d53cbf6450f356c10e436025d6d07", "problem_statement": "Remove \"for = ...\" from MultiWidget's