anon-iclr-submission/benchname-bug-localization huggingface dataset:
Repository: python-discord/bot
Issue: Message Filters Should Not Attempt to Delete Messages in DMs

Filters that attempt to delete offending message(s) should check whether or not the message was sent in a DM, since the bot cannot delete others' DM messages.

```
Forbidden: 403 Forbidden (error code: 50003): Cannot execute action on a DM channel
  File "discord/client.py", line 312, in _run_event
    await coro(*args, **kwargs)
  File "bot/cogs/filtering.py", line 114, in on_message
    await self._filter_message(msg)
  File "bot/cogs/filtering.py", line 176, in _filter_message
    await msg.delete()
  File "discord/message.py", line 762, in delete
    await self._state.http.delete_message(self.channel.id, self.id)
  File "discord/http.py", line 221, in request
    raise Forbidden(r, data)

Unhandled exception in on_message.
```

Sentry Issue: [BOT-3W](https://sentry.io/organizations/python-discord/issues/1650443849/?referrer=github_integration)

Changed files:
bot/cogs/filtering.py

diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 6a703f5a..1e21a4ce 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -4,7 +4,7 @@ from typing import Optional, Union
 
 import discord.errors
 from dateutil.relativedelta import relativedelta
-from discord import Colour, DMChannel, Member, Message, TextChannel
+from discord import Colour, Member, Message, TextChannel
 from discord.ext.commands import Cog
 from discord.utils import escape_markdown
 
@@ -161,8 +161,10 @@ class Filtering(Cog):
                         match = await _filter["function"](msg)
 
                     if match:
-                        # If this is a filter (not a watchlist), we should delete the message.
-                        if _filter["type"] == "filter":
+                        is_private = msg.channel.type is discord.ChannelType.private
+
+                        # If this is a filter (not a watchlist) and not in a DM, delete the message.
+                        if _filter["type"] == "filter" and not is_private:
                             try:
                                 # Embeds (can?) trigger both the `on_message` and `on_message_edit`
                                 # event handlers, triggering filtering twice for the same message.
@@ -181,7 +183,7 @@ class Filtering(Cog):
                             if _filter["user_notification"]:
                                 await self.notify_member(msg.author, _filter["notification_msg"], msg.channel)
 
-                        if isinstance(msg.channel, DMChannel):
+                        if is_private:
                             channel_str = "via DM"
                         else:
                             channel_str = f"in {msg.channel.mention}"
Repository: python-discord/bot
Issue: Tag cog crashes bot on systems with non utf8 default encoding.

The tag cog leaves out encoding when opening files, assuming the default is UTF8  but that is not the case on some OSs and fails with the `UnicodeDecodeError`.

The offending block of code can be found here:
https://github.com/python-discord/bot/blob/7571cabe65e39d231523e713923cd23b927225bc/bot/cogs/tags.py#L43-L48

paging @kmonteith25 here as they mentioned the issue in #dev-contrib

Changed files:
bot/cogs/tags.py

diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py
index 9ba33d7e..a813ffff 100644
--- a/bot/cogs/tags.py
+++ b/bot/cogs/tags.py
@@ -43,7 +43,7 @@ class Tags(Cog):
             tag = {
                 "title": tag_title,
                 "embed": {
-                    "description": file.read_text()
+                    "description": file.read_text(encoding="utf-8")
                 }
             }
             cache[tag_title] = tag
Repository: python-discord/bot
Issue: Set event loop to Selector on windows

With the [3.8 python update](https://docs.python.org/3/whatsnew/3.8.html#asyncio), the default event loop on windows is now the `ProactorEventLoop`; one of aiohttp's dependencies uses features which are not supported in this event loop leading to crashes on startup on windows with an undescriptive error

```
From cffi callback <function _sock_state_cb at 0x0000000003816040>:
Traceback (most recent call last):
  File "C:\\Users\\Numerlor\\.virtualenvs\\bot-K_QdelnW\\lib\\site-packages\\pycares\\__init__.py", line 91, in _sock_state_cb
    sock_state_cb(socket_fd, readable, writable)
  File "C:\\Users\\Numerlor\\.virtualenvs\\bot-K_QdelnW\\lib\\site-packages\\aiodns\\__init__.py", line 105, in _sock_state_cb
    self.loop.add_reader(fd, self._handle_event, fd, READ)
  File "C:\\Users\\Numerlor\\AppData\\Local\\Programs\\Python\\Python38\\Lib\\asyncio\\events.py", line 501, in add_reader
    raise NotImplementedError
NotImplementedError
```
This can be fixed by setting the event loop policy to `WindowsSelectorEventLoopPolicy` through `asyncio.set_event_loop_policy` when the platform is detected to be windows

Changed files:
bot/bot.py, bot/__init__.py

diff --git a/bot/__init__.py b/bot/__init__.py
index 2dd4af22..4131b69e 100644
--- a/bot/__init__.py
+++ b/bot/__init__.py
@@ -1,3 +1,4 @@
+import asyncio
 import logging
 import os
 import sys
@@ -59,3 +60,8 @@ coloredlogs.install(logger=root_log, stream=sys.stdout)
 logging.getLogger("discord").setLevel(logging.WARNING)
 logging.getLogger("websockets").setLevel(logging.WARNING)
 logging.getLogger(__name__)
+
+
+# On Windows, the selector event loop is required for aiodns.
+if os.name == "nt":
+    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
diff --git a/bot/bot.py b/bot/bot.py
index 49fac27e..a85a22aa 100644
--- a/bot/bot.py
+++ b/bot/bot.py
@@ -76,7 +76,7 @@ class Bot(commands.Bot):
             await self._resolver.close()
 
         if self.stats._transport:
-            await self.stats._transport.close()
+            self.stats._transport.close()
 
     async def login(self, *args, **kwargs) -> None:
         """Re-create the connector and set up sessions before logging into Discord."""
Repository: python-discord/bot
Issue: Modlog Events Should Check for DM

At least one of the event handlers in the modlog do not properly account for messages being sent in DMs & attempt to perform a guild ID comparison, which doesn't exist in a DM. This should be guarded against across the cog.

```
AttributeError: 'NoneType' object has no attribute 'id'
  File "discord/client.py", line 312, in _run_event
    await coro(*args, **kwargs)
  File "bot/cogs/moderation/modlog.py", line 555, in on_message_delete
    if message.guild.id != GuildConstant.id or channel.id in GuildConstant.modlog_blacklist:

Unhandled exception in on_message_delete.
```

Sentry Issue: [BOT-44](https://sentry.io/organizations/python-discord/issues/1656406094/?referrer=github_integration)

Changed files:
bot/cogs/moderation/modlog.py

diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py
index 9d28030d..41472c64 100644
--- a/bot/cogs/moderation/modlog.py
+++ b/bot/cogs/moderation/modlog.py
@@ -555,6 +555,10 @@ class ModLog(Cog, name="ModLog"):
         channel = message.channel
         author = message.author
 
+        # Ignore DMs.
+        if not message.guild:
+            return
+
         if message.guild.id != GuildConstant.id or channel.id in GuildConstant.modlog_blacklist:
             return
Repository: python-discord/bot
Issue: Invite Filter Raises on Invalid Invites

Currently, if the [`_has_invites`](https://github.com/python-discord/bot/blob/007f771b9cdf9de608920feea7f0e4a98b61fcab/bot/cogs/filtering.py#L290) catches an invalid/expired/etc. link it returns `True`, which motors down the filtering logic to a point where we're expecting a dictionary (a valid guild response from Discord's API):

https://github.com/python-discord/bot/blob/007f771b9cdf9de608920feea7f0e4a98b61fcab/bot/cogs/filtering.py#L215-L217

Since booleans don't have items, this will raise an `AttributeError`

The following logic block needs to be adjusted to prevent this issue from occurring:

https://github.com/python-discord/bot/blob/007f771b9cdf9de608920feea7f0e4a98b61fcab/bot/cogs/filtering.py#L314-L318

There are two approaches for resolution here:
  * Rather than return `True`, this conditional should generate a dictionary that's compatible with the above embed generation logic, indicating that an invite was provided but it's invalid
  * Return `False` instead, since we probably don't care about invite links that don't resolve

Changed files:
bot/cogs/filtering.py

diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 1e21a4ce..1d9fddb1 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -214,7 +214,9 @@ class Filtering(Cog):
                         additional_embeds = None
                         additional_embeds_msg = None
 
-                        if filter_name == "filter_invites":
+                        # The function returns True for invalid invites.
+                        # They have no data so additional embeds can't be created for them.
+                        if filter_name == "filter_invites" and match is not True:
                             additional_embeds = []
                             for invite, data in match.items():
                                 embed = discord.Embed(description=(
Repository: python-discord/bot
Issue: HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body

Sentry Issue: [BOT-3CR](https://sentry.io/organizations/python-discord/issues/3793707044/?referrer=github_integration)

```
HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
In content: Must be 2000 or fewer in length.
  File "discord/ext/tasks/__init__.py", line 239, in _loop
    await self.coro(*args, **kwargs)
  File "bot/exts/filters/filter_lists.py", line 308, in weekly_autoban_report_task
    await self.send_weekly_autoban_report()
  File "bot/exts/filters/filter_lists.py", line 346, in send_weekly_autoban_report
    await channel.send("\\n\\n".join(lines))
  File "discord/abc.py", line 1538, in send
    data = await state.http.send_message(channel.id, params=params)
  File "discord/http.py", line 744, in request
    raise HTTPException(response, data)

Unhandled exception in internal background task "weekly_autoban_report_task".
```

Changed files:
bot/exts/filtering/filtering.py

diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py
index 8e7ba747..aa53cab2 100644
--- a/bot/exts/filtering/filtering.py
+++ b/bot/exts/filtering/filtering.py
@@ -1,4 +1,5 @@
 import datetime
+import io
 import json
 import re
 import unicodedata
@@ -46,6 +47,7 @@ from bot.pagination import LinePaginator
 from bot.utils.channel import is_mod_channel
 from bot.utils.lock import lock_arg
 from bot.utils.message_cache import MessageCache
+from bot.utils.services import PasteTooLongError, PasteUploadError, send_to_paste_service
 
 log = get_logger(__name__)
 
@@ -1400,7 +1402,7 @@ class Filtering(Cog):
         """
         Send a list of auto-infractions added in the last 7 days to the specified channel.
 
-        If `channel` is not specified, it is sent to #mod-meta.
+        If `channel` is not specified, the report is sent to #mod-meta instead.
         """
         log.trace("Preparing weekly auto-infraction report.")
         seven_days_ago = arrow.utcnow().shift(days=-7)
@@ -1435,9 +1437,24 @@ class Filtering(Cog):
         if len(lines) == 1:
             lines.append("Nothing to show")
 
-        await channel.send("\\n\\n".join(lines))
-        log.info("Successfully sent auto-infraction report.")
+        report = "\\n\\n".join(lines)
+        try:
+            await channel.send(report)
+        except discord.HTTPException as e:
+            if e.code != 50035:  # Content too long
+                raise
+            report = discord.utils.remove_markdown(report)
+            try:
+                paste_resp = await send_to_paste_service(report, extension="txt")
+            except (ValueError, PasteTooLongError, PasteUploadError):
+                paste_resp = ":warning: Failed to upload report to paste service"
+            file_buffer = io.StringIO(report)
+            await channel.send(
+                f"**{lines[0]}**\\n\\n{paste_resp}",
+                file=discord.File(file_buffer, "last_weeks_autoban_filters.txt"),
+            )
 
+        log.info("Successfully sent auto-infraction report.")
     # endregion
 
     async def cog_unload(self) -> None:
Repository: python-discord/bot
Issue: Providing a single apostrophe-like character behind `!help` raises warning

Issuing an help command like `!help '` (a single single quote character as the help query) results in the following warning:

```py
2020-06-02 21:52:38 | root | WARNING | Applied processor reduces input query to empty string, all comparisons will have score 0. [Query: ''']
```

The same goes for at least  the queries:

```
[Query: '´']
[Query: '"']
[Query: '`']
```

The warning message hints at that such a "query" will be reduces to an empty string and, in turn, to a comparison score of `0`. The latter suggests that this warning is probably given by something in our fuzzy matching procedure. (But I haven't taken steps to confirm this yet.)

I think the best solution is to just show the normal help embed for such queries, but what the most robust way to do this is needs to be determined by the one who takes on this issue.

Sentry Issue: [BOT-5W](https://sentry.io/organizations/python-discord/issues/1705336367/?referrer=github_integration)

Changed files:
bot/cogs/help.py

diff --git a/bot/cogs/help.py b/bot/cogs/help.py
index 70e62d59..3d1d6fd1 100644
--- a/bot/cogs/help.py
+++ b/bot/cogs/help.py
@@ -8,6 +8,7 @@ from typing import List, Union
 from discord import Colour, Embed, Member, Message, NotFound, Reaction, User
 from discord.ext.commands import Bot, Cog, Command, Context, Group, HelpCommand
 from fuzzywuzzy import fuzz, process
+from fuzzywuzzy.utils import full_process
 
 from bot import constants
 from bot.constants import Channels, Emojis, STAFF_ROLES
@@ -145,7 +146,13 @@ class CustomHelpCommand(HelpCommand):
         Will return an instance of the `HelpQueryNotFound` exception with the error message and possible matches.
         """
         choices = await self.get_all_help_choices()
-        result = process.extractBests(string, choices, scorer=fuzz.ratio, score_cutoff=60)
+
+        # Run fuzzywuzzy's processor beforehand, and avoid matching if processed string is empty
+        # This avoids fuzzywuzzy from raising a warning on inputs with only non-alphanumeric characters
+        if (processed := full_process(string)):
+            result = process.extractBests(processed, choices, scorer=fuzz.ratio, score_cutoff=60, processor=None)
+        else:
+            result = []
 
         return HelpQueryNotFound(f'Query "{string}" not found.', dict(result))
Repository: python-discord/bot
Issue: Suppress NotFound exception when removing reactions for help command

Sentry Issue: [BOT-6X](https://sentry.io/organizations/python-discord/issues/1781203963/?referrer=github_integration)

```
TimeoutError: 
  File "bot/cogs/help.py", line 40, in help_cleanup
    await bot.wait_for("reaction_add", check=check, timeout=300)
  File "asyncio/tasks.py", line 490, in wait_for
    raise exceptions.TimeoutError()

NotFound: 404 Not Found (error code: 10008): Unknown Message
(3 additional frame(s) were not displayed)
...
  File "discord/ext/commands/help.py", line 817, in command_callback
    return await self.send_command_help(cmd)
  File "bot/cogs/help.py", line 203, in send_command_help
    await help_cleanup(self.context.bot, self.context.author, message)
  File "bot/cogs/help.py", line 43, in help_cleanup
    await message.remove_reaction(DELETE_EMOJI, bot.user)
  File "discord/message.py", line 982, in remove_reaction
    await self._state.http.remove_own_reaction(self.channel.id, self.id, emoji)
  File "discord/http.py", line 223, in request
    raise NotFound(r, data)

Error executing command invoked by <REDACTED>: !help purge user
```

Changed files:
bot/cogs/help.py

diff --git a/bot/cogs/help.py b/bot/cogs/help.py
index 832f6ea6..70e62d59 100644
--- a/bot/cogs/help.py
+++ b/bot/cogs/help.py
@@ -36,13 +36,12 @@ async def help_cleanup(bot: Bot, author: Member, message: Message) -> None:
 
     await message.add_reaction(DELETE_EMOJI)
 
-    try:
-        await bot.wait_for("reaction_add", check=check, timeout=300)
-        await message.delete()
-    except TimeoutError:
-        await message.remove_reaction(DELETE_EMOJI, bot.user)
-    except NotFound:
-        pass
+    with suppress(NotFound):
+        try:
+            await bot.wait_for("reaction_add", check=check, timeout=300)
+            await message.delete()
+        except TimeoutError:
+            await message.remove_reaction(DELETE_EMOJI, bot.user)
 
 
 class HelpQueryNotFound(ValueError):
Repository: python-discord/bot
Issue: Ensure Embed description is not Embed.Empty before stripping

Sentry Issue: [BOT-6K](https://sentry.io/organizations/python-discord/issues/1764434612/?referrer=github_integration)

It's a special value instead of simply an empty string, so trying to strip it may fail. Other code should also be reviewed to ensure it can't fail for similar reasons.

```
AttributeError: '_EmptyEmbed' object has no attribute 'strip'
  File "discord/client.py", line 312, in _run_event
    await coro(*args, **kwargs)
  File "bot/cogs/help_channels.py", line 719, in on_message_delete
    if not await self.is_empty(msg.channel):
  File "bot/cogs/help_channels.py", line 733, in is_empty
    return self.match_bot_embed(msg, AVAILABLE_MSG)
  File "bot/cogs/help_channels.py", line 443, in match_bot_embed
    return message.author == self.bot.user and embed.description.strip() == description.strip()

Unhandled exception in on_message_delete.
```

Changed files:
bot/cogs/help_channels.py

diff --git a/bot/cogs/help_channels.py b/bot/cogs/help_channels.py
index 0c8cbb41..e0fd0665 100644
--- a/bot/cogs/help_channels.py
+++ b/bot/cogs/help_channels.py
@@ -428,8 +428,11 @@ class HelpChannels(commands.Cog):
         if not message or not message.embeds:
             return False
 
-        embed = message.embeds[0]
-        return message.author == self.bot.user and embed.description.strip() == description.strip()
+        bot_msg_desc = message.embeds[0].description
+        if bot_msg_desc is discord.Embed.Empty:
+            log.trace("Last message was a bot embed but it was empty.")
+            return False
+        return message.author == self.bot.user and bot_msg_desc.strip() == description.strip()
 
     @staticmethod
     def is_in_category(channel: discord.TextChannel, category_id: int) -> bool:
Repository: python-discord/bot
Issue: User's avatar in `!user` is not animated.

## Problem
Right now, when doing `!user` the avatar is always set to `png`, even if the user's avatar is a gif.

## Proposal
Similar to how we are handling avatars in other places, instead of using the `format` keyword, we can use the `static_format` keyword instead, as [documented in discord.py](https://discordpy.readthedocs.io/en/latest/api.html#discord.User.avatar_url_as)

The code responsible for the command can be found here:
https://github.com/python-discord/bot/blob/fe325adc66e9621ea970abd83db58df4b792caa5/bot/cogs/information.py#L209-L212

You can see how filter is formating it here:
https://github.com/python-discord/bot/blob/fe325adc66e9621ea970abd83db58df4b792caa5/bot/cogs/filtering.py#L233-L243

Changed files:
bot/cogs/information.py, tests/bot/cogs/test_information.py

diff --git a/bot/cogs/information.py b/bot/cogs/information.py
index 4eb36c34..ef2f308c 100644
--- a/bot/cogs/information.py
+++ b/bot/cogs/information.py
@@ -206,7 +206,7 @@ class Information(Cog):
             description="\\n\\n".join(description)
         )
 
-        embed.set_thumbnail(url=user.avatar_url_as(format="png"))
+        embed.set_thumbnail(url=user.avatar_url_as(static_format="png"))
         embed.colour = user.top_role.colour if roles else Colour.blurple()
 
         return embed
diff --git a/tests/bot/cogs/test_information.py b/tests/bot/cogs/test_information.py
index 6dace108..b5f928dd 100644
--- a/tests/bot/cogs/test_information.py
+++ b/tests/bot/cogs/test_information.py
@@ -485,7 +485,7 @@ class UserEmbedTests(unittest.TestCase):
         user.avatar_url_as.return_value = "avatar url"
         embed = asyncio.run(self.cog.create_user_embed(ctx, user))
 
-        user.avatar_url_as.assert_called_once_with(format="png")
+        user.avatar_url_as.assert_called_once_with(static_format="png")
         self.assertEqual(embed.thumbnail.url, "avatar url")
Repository: miczflor/rpi-jukebox-rfid
Issue: 🐛 | VolumeUp / VolumeDown not working properly with GPIO Buttons since 2.1

## Bug
Since 2.1 and the new gpio buttons implementation the VolumeUp VolumeDown commands are not working properly. When I start and have a base sound level of 15 and I push the button up, it will become 63.

### What I did
I made a clean install (buster) to verify #1078 and ran into this issue. Having set up all as default and the buttons connected to the pins as mentioned in the old default setup guide.

### What happened
Sound is increased / decreased by wrong values

### Further information that might help
Debugged the playout_controls.sh and found out, that in the line with `VOLPERCENT=´expr ${VOLPERCENT} + \\( ${AUDIOVOLCHANGESTEP} \\* ${VALUE} \\)´` value seems to be the value of the defined pin (e.g. 16 or 19 for VolumeUp or VolumeDown)

## Software

### Base image and version
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

### Branch / Release
master /fresh version 2.1

### Installscript
* scripts/installscripts/buster-install-default.sh

## Hardware

### RaspberryPi version

Raspberry Pi 3 Model B Rev 1.2

### RFID Reader
`16c0:27db HXGCoLtd Keyboard`

### Soundcard
i.e. `0d8c:0014 C-Media Electronics, Inc. Audio Adapter (Unitek Y-247A)`

### Other notable hardware
default gpio_settings but with enabled VolumeUp / VolumeDown buttons


Changed files:
components/gpio_control/GPIODevices/simple_button.py

diff --git a/components/gpio_control/GPIODevices/simple_button.py b/components/gpio_control/GPIODevices/simple_button.py
index 199c5c8b..730f6573 100644
--- a/components/gpio_control/GPIODevices/simple_button.py
+++ b/components/gpio_control/GPIODevices/simple_button.py
@@ -74,6 +74,10 @@ class SimpleButton:
                               bouncetime=self.bouncetime)
 
     def callbackFunctionHandler(self, *args):
+        logger.info('args: {}'.format(*args))
+        if (len(args) > 0 and args[0] == self.pin):
+            args = args[1:]
+			
         if self.hold_repeat:
             return self.holdAndRepeatHandler(*args)
         logger.info('{}: executre callback'.format(self.name))
Repository: miczflor/rpi-jukebox-rfid
Issue: Wrong reference to a non-existing service

https://github.com/MiczFlor/RPi-Jukebox-RFID/blob/70f989b9a73768144452da1d5d681ce2f9ac99ca/components/smart-home-automation/MQTT-protocol/daemon_mqtt_client.py#L117

The GPIO Service has changed to phoniebox-gpio-control.service

Changed files:
components/smart-home-automation/MQTT-protocol/daemon_mqtt_client.py

diff --git a/components/smart-home-automation/MQTT-protocol/daemon_mqtt_client.py b/components/smart-home-automation/MQTT-protocol/daemon_mqtt_client.py
index 6794e5f8..4ccb639f 100644
--- a/components/smart-home-automation/MQTT-protocol/daemon_mqtt_client.py
+++ b/components/smart-home-automation/MQTT-protocol/daemon_mqtt_client.py
@@ -114,7 +114,7 @@ def processCmd(command, parameter):
     elif command == "gpio":
         parameter = parameter.lower()
         if parameter == "start" or parameter == "stop":
-            subprocess.call(["sudo /bin/systemctl " + parameter + " phoniebox-gpio-buttons.service"], shell=True)
+            subprocess.call(["sudo /bin/systemctl " + parameter + " phoniebox-gpio-control.service"], shell=True)
         else:
             print(" --> Expecting parameter start or stop")
 
@@ -331,7 +331,7 @@ def fetchData():
 
     # fetch service states
     result["rfid"] = isServiceRunning("phoniebox-rfid-reader.service")
-    result["gpio"] = isServiceRunning("phoniebox-gpio-buttons.service")
+    result["gpio"] = isServiceRunning("phoniebox-gpio-control.service")
 
     # fetch linux jobs
     result["remaining_stopafter"] = str(linux_job_remaining("s"))
Repository: wireservice/agate
Issue: Table.rename does not rename keys of Row items

Looks like Table.rename() calls Table.fork(), which calls Table.**init**() with _is_fork=True which skips the code in this if block. Because of that, the _keys attribute of each Row instance retains the old values.


Changed files:
agate/table.py, tests/test_table.py

diff --git a/agate/table.py b/agate/table.py
index 994cf52..2ce29c2 100644
--- a/agate/table.py
+++ b/agate/table.py
@@ -285,8 +285,14 @@ class Table(utils.Patchable):
 
         if isinstance(row_names, dict):
             row_names = [row_names[name] if name in row_names else name for name in self.row_names]
-
-        return self._fork(self.rows, column_names, self._column_types, row_names=row_names)
+        
+        if column_names is not None and column_names != self.column_names:
+            if row_names is None:
+                row_names = self._row_names
+                
+            return Table(self.rows, column_names, self.column_types, row_names=row_names, _is_fork=False)
+        else:
+            return self._fork(self.rows, column_names, self._column_types, row_names=row_names)
 
     @classmethod
     def from_csv(cls, path, column_names=None, column_types=None, row_names=None, header=True, **kwargs):
diff --git a/tests/test_table.py b/tests/test_table.py
index 615b7e7..246b7e8 100644
--- a/tests/test_table.py
+++ b/tests/test_table.py
@@ -1723,6 +1723,14 @@ class TestData(AgateTestCase):
 
         self.assertIs(table.row_names, None)
         self.assertSequenceEqual(table.column_names, self.column_names)
+    
+    def test_rename_column_names_renames_row_values(self):
+        table = Table(self.rows, self.column_names, self.column_types)
+        new_column_names = ['d', 'e', 'f']
+        table2 = table.rename(column_names=new_column_names)
+        for row, row2 in zip(table.rows, table2.rows):
+            for key, key2 in zip(self.column_names, new_column_names):
+                self.assertEqual(row[key], row2[key2])
 
 
 class TableHTMLParser(html_parser.HTMLParser):
Repository: wireservice/agate
Issue: agate.All cannot test whether all data is False

If the column data type is boolean, test gets overwritten to search for True values.


Changed files:
agate/aggregations/any.py, tests/test_aggregations.py

diff --git a/agate/aggregations/any.py b/agate/aggregations/any.py
index 70fa702..67a9651 100644
--- a/agate/aggregations/any.py
+++ b/agate/aggregations/any.py
@@ -32,7 +32,7 @@ class Any(Aggregation):
         column = table.columns[self._column_name]
         data = column.values()
 
-        if isinstance(column.data_type, Boolean):
+        if isinstance(column.data_type, Boolean) and self._test is None:
             return any(data)
 
         return any(self._test(d) for d in data)
diff --git a/tests/test_aggregations.py b/tests/test_aggregations.py
index c3c8fbb..11eefe1 100644
--- a/tests/test_aggregations.py
+++ b/tests/test_aggregations.py
@@ -138,6 +138,7 @@ class TestBooleanAggregation(unittest.TestCase):
         table = Table(rows, ['test'], [Boolean()])
         Any('test').validate(table)
         self.assertEqual(Any('test').run(table), False)
+        self.assertEqual(Any('test', lambda r: not r).run(table), True)
 
     def test_all(self):
         rows = [