nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: elixir-ecto/ecto
Pull request #4432 (2024-06-14 23:24:40)

Convert structs that implement the Enumerable protocol to lists
I found it surprising that I could not use a `MapSet` or another `Enum` when using `in` in a query as I expected it to function similarly to `Kernel.in/2`.
```elixir
ids = MapSet.new([1, 2, 3])
where(Comment, [c], c.id in ^ids) 
```

This PR checks for an implementation of the `Enumerable` protocol and converts it to a list when casting, returning an error if there is none.

If this isn't the direction you want Ecto to go, I'm happy to have this closed. Otherwise, let me know anything else I should be mindful of or any documentation I should update.

Thanks!

greg-rychlewski: Thanks. It would be good to update the docs here: https://hexdocs.pm/ecto/Ecto.Query.API.html#in/2

Patch:
diff --git a/lib/ecto/query/api.ex b/lib/ecto/query/api.ex
index 6cb29205..46813d4f 100644
--- a/lib/ecto/query/api.ex
+++ b/lib/ecto/query/api.ex
@@ -135,8 +135,9 @@ defmodule Ecto.Query.API do
 
       from p in Post, where: p.id in [1, 2, 3]
 
-  The right side may either be a list, a literal list
-  or even a column in the database with array type:
+  The right side may either be a literal list, an interpolated list,
+  any struct that implements the `Enumerable` protocol, or even a
+  column in the database with array type:
 
       from p in Post, where: "elixir" in p.tags
 
diff --git a/lib/ecto/type.ex b/lib/ecto/type.ex
index 3a656c1e..dc0b0ccf 100644
--- a/lib/ecto/type.ex
+++ b/lib/ecto/type.ex
@@ -1387,6 +1387,13 @@ defmodule Ecto.Type do
     {:ok, Enum.reverse(acc)}
   end
 
+  defp array_with_index(%_{} = struct, fun, skip_nil?, index, acc) do
+    case Enumerable.impl_for(struct) do
+      nil -> :error
+      _ -> struct |> Enum.to_list() |> array_with_index(fun, skip_nil?, index, acc)
+    end
+  end
+
   defp array_with_index(_, _, _, _, _) do
     :error
   end


Test patch:
diff --git a/test/ecto/type_test.exs b/test/ecto/type_test.exs
index 13f2284f..a4aa8b3c 100644
--- a/test/ecto/type_test.exs
+++ b/test/ecto/type_test.exs
@@ -243,6 +243,8 @@ defmodule Ecto.TypeTest do
 
   test "in" do
     assert cast({:in, :integer}, ["1", "2", "3"]) == {:ok, [1, 2, 3]}
+    assert {:ok, list} = cast({:in, :integer}, MapSet.new(~w(1 2 3)))
+    assert [1, 2, 3] = Enum.sort(list)
     assert cast({:in, :integer}, nil) == :error
   end
Repository: elixir-ecto/ecto
Pull request #4446 (2024-07-10 11:19:06)

Improve error message on invalid Ecto.Type.cast/1
Ref: #4444

Another idea for implementation would be to raise from `Ecto.Type.cast/2`.


Patch:
diff --git a/lib/ecto/query/planner.ex b/lib/ecto/query/planner.ex
index 6e9616d5..18005e50 100644
--- a/lib/ecto/query/planner.ex
+++ b/lib/ecto/query/planner.ex
@@ -2537,9 +2537,17 @@ defmodule Ecto.Query.Planner do
       {:ok, v} ->
         {:ok, v}
 
-      _ ->
+      :error ->
         {:error,
          "value `#{inspect(v)}` in `#{kind}` cannot be cast to type #{Ecto.Type.format(type)}"}
+
+      {:error, _meta} ->
+        {:error,
+         "value `#{inspect(v)}` in `#{kind}` cannot be cast to type #{Ecto.Type.format(type)}"}
+
+      other ->
+        raise "expected #{inspect(type)}.cast/1 to return {:ok, v}, :error, or {:error, meta}" <>
+                ", got: #{inspect(other)}"
     end
   end
 


Test patch:
diff --git a/test/ecto/repo_test.exs b/test/ecto/repo_test.exs
index b7fa3af1..4a8b4486 100644
--- a/test/ecto/repo_test.exs
+++ b/test/ecto/repo_test.exs
@@ -1792,6 +1792,7 @@ defmodule Ecto.RepoTest do
       use Ecto.Type
       def type(), do: :binary_id
       def cast("foo-" <> _ = id), do: {:ok, id}
+      def cast("invalid"), do: :invalid
       def cast(id), do: {:ok, "foo-" <> id}
       def load(uuid), do: {:ok, "foo-" <> uuid}
       def dump("foo-" <> uuid), do: {:ok, uuid}
@@ -1818,6 +1819,15 @@ defmodule Ecto.RepoTest do
       assert {:ok, inserted} = TestRepo.insert(changeset)
       assert inserted.id == "foo-" <> id
     end
+
+    test "invalid Ecto.Type.cast/1 implementation raises" do
+      e =
+        assert_raise RuntimeError, fn ->
+          TestRepo.all(from s in MySchemaCustomPK, where: s.id == ^"invalid")
+        end
+
+      assert e.message =~ "expected Ecto.RepoTest.PrefixedID.cast/1 to return {:ok, v},"
+    end
   end
 
   describe "transactions" do
Repository: elixir-ecto/ecto
Pull request #4461 (2024-07-25 02:47:37)

Fix inspecting fields from sources with take
There is an issue when using inspect on a query that selects a field from a source that also has `take`. The traversal only looks for `{:&, _, [ix]}` and prints out `map(...)` or `struct(...)` if take is present without considering the context `{:&, _, [ix]}` is being used in.

The change here is to catch during the pre-order traversal when it is being used  as part of a field expression. Because in this case it does not make sense to consider `take`. 

Without this change the test I added would print ` "from p0 in Inspect.Post, select: merge(%{title: map(p0, [:visits]).title}, map(p0, [:visits]))"`


Patch:
diff --git a/lib/ecto/query/inspect.ex b/lib/ecto/query/inspect.ex
index e9e992a7..3911c9ed 100644
--- a/lib/ecto/query/inspect.ex
+++ b/lib/ecto/query/inspect.ex
@@ -245,7 +245,7 @@ defimpl Inspect, for: Ecto.Query do
   @doc false
   def expr(expr, names, part) do
     expr
-    |> Macro.traverse(:ok, &{prewalk(&1), &2}, &{postwalk(&1, names, part), &2})
+    |> Macro.traverse(:ok, &{prewalk(&1, names), &2}, &{postwalk(&1, names, part), &2})
     |> elem(0)
     |> macro_to_string()
   end
@@ -253,15 +253,19 @@ defimpl Inspect, for: Ecto.Query do
   defp macro_to_string(expr), do: Macro.to_string(expr)
 
   # Tagged values
-  defp prewalk(%Ecto.Query.Tagged{value: value, tag: nil}) do
+  defp prewalk(%Ecto.Query.Tagged{value: value, tag: nil}, _) do
     value
   end
 
-  defp prewalk(%Ecto.Query.Tagged{value: value, tag: tag}) do
+  defp prewalk(%Ecto.Query.Tagged{value: value, tag: tag}, _) do
     {:type, [], [value, tag]}
   end
 
-  defp prewalk(node) do
+  defp prewalk({{:., dot_meta, [{:&, _, [ix]}, field]}, meta, []}, names) do
+    {{:., dot_meta, [binding(names, ix), field]}, meta, []}
+  end
+
+  defp prewalk(node, _) do
     node
   end
 


Test patch:
diff --git a/test/ecto/query/inspect_test.exs b/test/ecto/query/inspect_test.exs
index 2253ba81..8ae7040a 100644
--- a/test/ecto/query/inspect_test.exs
+++ b/test/ecto/query/inspect_test.exs
@@ -477,6 +477,9 @@ defmodule Ecto.Query.InspectTest do
 
     assert i(from(p in Post, select: merge(p, %{foo: p.foo}))) ==
              ~s"from p0 in Inspect.Post, select: merge(p0, %{foo: p0.foo})"
+
+    assert i(from(p in Post, select: %{title: p.title}, select_merge: map(p, [:visits]))) ==
+             ~s"from p0 in Inspect.Post, select: merge(%{title: p0.title}, map(p0, [:visits]))"
   end
 
   test "select after planner" do
Repository: elixir-ecto/ecto
Pull request #4523 (2024-10-08 15:38:52)

Raise when empty list is given to `values/2`
Closes https://github.com/elixir-ecto/ecto/issues/4522

fuelen: I think it would be great to mention this in docs as well. It is not obvious that empty list must be handled explicitly. I caught this error in production.
greg-rychlewski: @fuelen Thank you that's a good callout. I will update the docs.

Out of curiosity do you know of any other standard way that this is handled? 
fuelen: > Out of curiosity do you know of any other standard way that this is handled?

If we talk about elegant ways, then I don't know any 

Patch:
diff --git a/lib/ecto/query.ex b/lib/ecto/query.ex
index 4168c49d..5af3cd3f 100644
--- a/lib/ecto/query.ex
+++ b/lib/ecto/query.ex
@@ -485,6 +485,10 @@ defmodule Ecto.Query do
     @moduledoc false
     defstruct [:types, :num_rows, :params]
 
+    def new([], _types) do
+      raise ArgumentError, "must provide a non-empty list to values/2"
+    end
+
     def new(values_list, types) do
       fields = fields(values_list)
       types = types!(fields, types)
diff --git a/lib/ecto/query/api.ex b/lib/ecto/query/api.ex
index 0ce9f8bc..9d2c6ce5 100644
--- a/lib/ecto/query/api.ex
+++ b/lib/ecto/query/api.ex
@@ -515,7 +515,8 @@ defmodule Ecto.Query.API do
   and `Ecto.Query.join/5`.
 
   The first argument is a list of maps representing the values of the constant table.
-  Each entry in the list must have exactly the same fields or an error is raised.
+  An error is raised if the list is empty or if every map does not have exactly the
+  same fields.
 
   The second argument is a map of types corresponding to the fields in the first argument.
   Each field must be given a type or an error is raised. Any type that can be specified in


Test patch:
diff --git a/test/ecto/query/builder/from_test.exs b/test/ecto/query/builder/from_test.exs
index b6f4eeac..8114e587 100644
--- a/test/ecto/query/builder/from_test.exs
+++ b/test/ecto/query/builder/from_test.exs
@@ -27,6 +27,14 @@ defmodule Ecto.Query.Builder.FromTest do
     types_kw = Enum.map(types, & &1)
     assert query.from.source == {:values, [], [types_kw, length(values)]}
 
+    # Empty values
+    msg = "must provide a non-empty list to values/2"
+
+    assert_raise ArgumentError, msg, fn ->
+      from v in values([], %{})
+    end
+
+
     # Missing type
     msg = "values/2 must declare the type for every field. The type was not given for field `text`"
Repository: elixir-ecto/ecto
Pull request #4548 (2024-11-12 22:03:56)

Plan sources before creating plan_subquery closure
Closes https://github.com/elixir-ecto/ecto/issues/4547

Because `where` and friends might need to cast parameters to field types, we need to be able to cast the sources first. This way `query.sources` is populated.


Patch:
diff --git a/lib/ecto/query/planner.ex b/lib/ecto/query/planner.ex
index b4d16403..ce57afe2 100644
--- a/lib/ecto/query/planner.ex
+++ b/lib/ecto/query/planner.ex
@@ -221,10 +221,10 @@ defmodule Ecto.Query.Planner do
           {planned_query :: Ecto.Query.t(), parameters :: list(), cache_key :: any()}
   def plan(query, operation, adapter, cte_names \\ %{}) do
     {query, cte_names} = plan_ctes(query, adapter, cte_names)
+    query = plan_sources(query, adapter, cte_names)
     plan_subquery = &plan_subquery(&1, query, nil, adapter, false, cte_names)
 
     query
-    |> plan_sources(adapter, cte_names)
     |> plan_assocs()
     |> plan_combinations(adapter, cte_names)
     |> plan_expr_subqueries(:wheres, plan_subquery)


Test patch:
diff --git a/test/ecto/query/subquery_test.exs b/test/ecto/query/subquery_test.exs
index 56d4dac2..c9c0e389 100644
--- a/test/ecto/query/subquery_test.exs
+++ b/test/ecto/query/subquery_test.exs
@@ -481,6 +481,12 @@ defmodule Ecto.Query.SubqueryTest do
       assert dump_params == ["foo"]
     end
 
+    test "in query with exists" do
+      c = from(c in "comments", where: ^"title" == parent_as(:p).title, select: 1)
+      s = from(p in "posts", as: :p, where: exists(c), select: count())
+      normalize(s)
+    end
+
     test "in dynamic" do
       c = from(c in Comment, where: c.text == ^"foo", select: c.post_id)
       d = dynamic([p], p.id in subquery(c))
Repository: elixir-ecto/ecto
Pull request #4592 (2025-03-14 14:38:55)

Fix join_count conflicts
Closes https://github.com/elixir-ecto/ecto/issues/4591

To me it looks like this is happening:

1. The `from` source can only be determined at runtime, so we don't know the starting join count. We quote the expression used to evaluate the join count.
2. The count from (1) is stored in a variable `join_count` in the join builder module.
3. Now let's say we have a subquery that also has a `from` source that can only be determined at runtime. This is setting the variable `join_count` to what makes sense for it. And this is conflicting with (2)

So the solution I made is to not set a `join_count` variable and instead evaluate the `Builder.count_binds` function each time.


Patch:
diff --git a/lib/ecto/query/builder/join.ex b/lib/ecto/query/builder/join.ex
index 43de1cb0..4d235347 100644
--- a/lib/ecto/query/builder/join.ex
+++ b/lib/ecto/query/builder/join.ex
@@ -165,11 +165,9 @@ defmodule Ecto.Query.Builder.Join do
       if is_nil(count_bind) do
         query =
           quote do
-            query = Ecto.Queryable.to_query(unquote(query))
-            join_count = Builder.count_binds(query)
-            query
+            Ecto.Queryable.to_query(unquote(query))
           end
-        {quote(do: join_count), query}
+        {quote(do: Builder.count_binds(unquote(query))), query}
       else
         {count_bind, query}
       end


Test patch:
diff --git a/test/ecto/query/builder/join_test.exs b/test/ecto/query/builder/join_test.exs
index 32d8fc64..fc95f61b 100644
--- a/test/ecto/query/builder/join_test.exs
+++ b/test/ecto/query/builder/join_test.exs
@@ -202,4 +202,17 @@ defmodule Ecto.Query.Builder.JoinTest do
       end, [], __ENV__)
     end
   end
+
+  test "join count is incremented correctly for runtime sources" do
+    users_table = "users"
+    source_query = fn -> from p in "posts", join: c in "comments", on: true end
+
+    q =
+      from s in source_query.(),
+        join: j in subquery(from u in users_table, join: p in "profiles", on: true, select: u.id),
+        on: true,
+        select: {s.id, j.id}
+
+    assert {:{}, _, [_, {{:., [], [{:&, [], [2]}, :id]}, [], []}]} = q.select.expr
+  end
 end
Repository: elixir-ecto/ecto
Pull request #4624 (2025-06-21 07:19:42)

Add support for :writable option on belongs_to
PR for my [proposal](https://groups.google.com/g/elixir-ecto/c/JDL69kx5nTA) to add `:writable` option to `belongs_to`.

This PR simply whitelists the `:writable` option in `@valid_belongs_to_options`, which has already been implemented in #4335 & #4472.

`cast_assoc` & `put_assoc` currently do not respect this option, so if it makes sense, I can send a follow-up PR for that.

josevalim: Looks good to me, but I wonder if @greg-rychlewski has any additional thoughts?

> cast_assoc & put_assoc currently do not respect this option, so if it makes sense, I can send a follow-up PR for that.

How do you think it would be handled? I can still see someone using cast_assoc for something else, even though they never write the field, so perhaps we should not change anything.

Patch:
diff --git a/lib/ecto/schema.ex b/lib/ecto/schema.ex
index 6663730c..f28ea693 100644
--- a/lib/ecto/schema.ex
+++ b/lib/ecto/schema.ex
@@ -1106,6 +1106,12 @@ defmodule Ecto.Schema do
     * `:where` - A filter for the association. See "Filtering associations"
       in `has_many/3`.
 
+    * `:writable` - Defines when the underlying field is allowed to modified, Must be
+      one of `:always`, `:insert`, or `:never`. If set to `:always`, the field can be
+      modified by any repo operation. If set to `:insert`, the field can be inserted
+      but cannot be further modified, even in an upsert. If set to `:never`, the field
+      becomes read only. Defaults to `:always`.
+
   ## Examples
 
       defmodule Comment do
@@ -2138,6 +2144,7 @@ defmodule Ecto.Schema do
     :defaults,
     :primary_key,
     :source,
+    :writable,
     :where
   ]
 


Test patch:
diff --git a/test/ecto/repo/belongs_to_test.exs b/test/ecto/repo/belongs_to_test.exs
index c83cec8d..81e2440b 100644
--- a/test/ecto/repo/belongs_to_test.exs
+++ b/test/ecto/repo/belongs_to_test.exs
@@ -35,6 +35,15 @@ defmodule Ecto.Repo.BelongsToTest do
     end
   end
 
+  defmodule RestrictedSchema do
+    use Ecto.Schema
+
+    schema "restricted_schema" do
+      belongs_to :unwritable, MyAssoc, writable: :never
+      belongs_to :non_updatable, MyAssoc, writable: :insert
+    end
+  end
+
   test "handles assocs on insert" do
     sample = %MyAssoc{x: "xyz"}
 
@@ -496,4 +505,29 @@ defmodule Ecto.Repo.BelongsToTest do
     assert updated_schema.assoc_id == 2
     assert %Ecto.Association.NotLoaded{} = updated_schema.assoc
   end
+
+  test "handles writable: :never" do
+    schema =
+      %RestrictedSchema{}
+      |> Ecto.Changeset.cast(%{unwritable_id: 111}, [:unwritable_id])
+      |> TestRepo.insert!()
+
+    assert schema.unwritable_id == nil
+  end
+
+  test "handles writable: :insert" do
+    schema =
+      %RestrictedSchema{}
+      |> Ecto.Changeset.cast(%{non_updatable_id: 222}, [:non_updatable_id])
+      |> TestRepo.insert!()
+
+    assert schema.non_updatable_id == 222
+
+    schema =
+      schema
+      |> Ecto.Changeset.cast(%{non_updatable_id: 333}, [:non_updatable_id])
+      |> TestRepo.update!()
+
+    assert schema.non_updatable_id == 222
+  end
 end
Repository: line/line-bot-sdk-python
Pull request #41 (2017-04-28 07:31:48)

Fix serialization error of Beacon model.
`__str__` and `__repr__` of `Base` class uses json serialization.
But `bytearray` cannot be json serialized.

okdtsk: LGMT :)

Patch:
diff --git a/examples/flask-kitchensink/app.py b/examples/flask-kitchensink/app.py
index f6f2d7f..2d7c425 100644
--- a/examples/flask-kitchensink/app.py
+++ b/examples/flask-kitchensink/app.py
@@ -250,8 +250,9 @@ def handle_postback(event):
 def handle_beacon(event):
     line_bot_api.reply_message(
         event.reply_token,
-        TextSendMessage(text='Got beacon event. hwid={}, device_message={}'
-                             .format(event.beacon.hwid, event.beacon.device_message)))
+        TextSendMessage(
+            text='Got beacon event. hwid={}, device_message(hex string)={}'.format(
+                event.beacon.hwid, event.beacon.dm)))
 
 
 if __name__ == "__main__":
diff --git a/linebot/models/events.py b/linebot/models/events.py
index 2261a42..65711b8 100644
--- a/linebot/models/events.py
+++ b/linebot/models/events.py
@@ -287,11 +287,20 @@ class Beacon(Base):
 
         :param str type: Type of beacon event
         :param str hwid: Hardware ID of the beacon that was detected
-        :param str dm: Optional. Device message of beacon that was detected in bytearray
+        :param str dm: Optional. Device message of beacon which is hex string
         :param kwargs:
         """
         super(Beacon, self).__init__(**kwargs)
 
         self.type = type
         self.hwid = hwid
-        self.device_message = bytearray.fromhex(dm) if dm is not None else None
+        self.dm = dm
+
+    @property
+    def device_message(self):
+        """Get dm(device_message) as bytearray.
+
+        :rtype: bytearray
+        :return:
+        """
+        return bytearray.fromhex(self.dm) if self.dm is not None else None


Test patch:
diff --git a/tests/test_webhook.py b/tests/test_webhook.py
index ec6cca8..bbd0a93 100644
--- a/tests/test_webhook.py
+++ b/tests/test_webhook.py
@@ -204,6 +204,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertEqual(events[11].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
         self.assertEqual(events[11].beacon.hwid, 'd41d8cd98f')
         self.assertEqual(events[11].beacon.type, 'enter')
+        self.assertEqual(events[11].beacon.dm, None)
         self.assertEqual(events[11].beacon.device_message, None)
 
         # BeaconEvent, SourceUser (with device message)
@@ -217,6 +218,7 @@ class TestWebhookParser(unittest.TestCase):
         self.assertEqual(events[12].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8')
         self.assertEqual(events[12].beacon.hwid, 'd41d8cd98f')
         self.assertEqual(events[12].beacon.type, 'enter')
+        self.assertEqual(events[12].beacon.dm, '1234567890abcdef')
         self.assertEqual(events[12].beacon.device_message, bytearray(b'\x124Vx\x90\xab\xcd\xef'))
Repository: line/line-bot-sdk-python
Pull request #112 (2018-07-02 03:15:43)

Improve __str__ of LineBotApiError


Patch:
diff --git a/linebot/exceptions.py b/linebot/exceptions.py
index e36a366..58c9b84 100644
--- a/linebot/exceptions.py
+++ b/linebot/exceptions.py
@@ -73,3 +73,12 @@ class LineBotApiError(BaseError):
 
         self.status_code = status_code
         self.error = error
+
+    def __str__(self):
+        """str.
+
+        :rtype: str
+        :return:
+        """
+        return '{0}: status_code={1}, error_response={2}'.format(
+            self.__class__.__name__, self.status_code, self.error)
diff --git a/linebot/models/flex_message.py b/linebot/models/flex_message.py
index a4bbcc6..44a28d9 100644
--- a/linebot/models/flex_message.py
+++ b/linebot/models/flex_message.py
@@ -132,7 +132,6 @@ class BubbleStyle(with_metaclass(ABCMeta, Base)):
         """
         super(BubbleStyle, self).__init__(**kwargs)
 
-        self.type = 'carousel'
         self.header = self.get_or_new_from_json_dict(header, BlockStyle)
         self.hero = self.get_or_new_from_json_dict(hero, BlockStyle)
         self.body = self.get_or_new_from_json_dict(body, BlockStyle)


Test patch:
diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py
new file mode 100644
index 0000000..61a37ee
--- /dev/null
+++ b/tests/test_exceptions.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+
+#  Licensed under the Apache License, Version 2.0 (the "License"); you may
+#  not use this file except in compliance with the License. You may obtain
+#  a copy of the License at
+#
+#       https://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#  License for the specific language governing permissions and limitations
+#  under the License.
+
+from __future__ import unicode_literals, absolute_import
+
+import unittest
+
+from linebot.exceptions import LineBotApiError
+from linebot.models import Error, ErrorDetail
+
+
+class TestUtils(unittest.TestCase):
+    def test_str(self):
+        line_bot_api_error = LineBotApiError(
+            400,
+            error=Error(message='The request body has 1 error(s)',
+                        details=[ErrorDetail(message='May not be empty',
+                                             property='messages[0].text')]))
+        self.assertEqual(line_bot_api_error.__str__(),
+                         'LineBotApiError: status_code=400, error_response={"details": '
+                         '[{"message": "May not be empty", "property": "messages[0].text"}], '
+                         '"message": "The request body has 1 error(s)"}')
+
+
+if __name__ == '__main__':
+    unittest.main()