Code editing task: filesystem
Topic: Misc; change kind: corrective

Original code:
from typing import Callable, List
from abc import ABC, abstractmethod


class File(ABC):
    """
    Represents a file in the file system.
    """

    def __init__(self, name: str, permissions: int, owner: str):
        assert 0 <= permissions <= 0o777, "Invalid permissions..."
        self.name = name
        self.permissions = permissions
        self.owner = owner

    @abstractmethod
    def map_content(self, function: Callable[[str], str]) -> "File":
        """
        Maps over the content of regular files, and just traverses the rest of the file system.
        Does not follow links. The function is applied to the content of the file.
        """
        pass

    @abstractmethod
    def map_files(self, function: Callable[["File"], None]):
        """
        Maps over all the files and directories in the file system. Does not follow links.
        Changes are done in-place.
        """
        pass


class RegularFile(File):
    """
    Represents a regular file in the file system, which is just a file with some content inside.
    """

    def __init__(self, name: str, permissions: int, owner: str, content: str):
        super().__init__(name, permissions, owner)
        self.content = content

    def map_content(self, function: Callable[[str], str]) -> "RegularFile":
        return RegularFile(self.name, self.permissions, self.owner, function(self.content))

    def map_files(self, function: Callable[["File"], None]):
        function(self)

    def __eq__(self, other):
        return self.name == other.name and self.permissions == other.permissions and self.owner == other.owner and self.content == other.content


class Directory(File):
    """
    Represents a directory in the file system, which is basically a file with a list of files.
    """

    def __init__(self, name: str, permissions: int, owner: str, files: List[File]):
        super().__init__(name, permissions, owner)
        self.files = files

    def map_content(self, function: Callable[[str], str]) -> "Directory":
        return Directory(self.name, self.permissions, self.owner, [function(file) for file in self.files])

    def map_files(self, function: Callable[["File"], None]):
        function(self)

    def __eq__(self, other):
        return self.name == other.name and self.permissions == other.permissions and self.owner == other.owner and all(
            a == b for a, b in zip(self.files, other.files))

Instruction (descriptive):
Fix map_files and map_content in Directory, both functions are not traversing the files in the directory correctly, they should call the function recursively for each file in the directory.

Instruction (lazy):
Fix both map implementations for Directory, they don't respect the docstring.

Edited code:
from typing import Callable, List
from abc import ABC, abstractmethod


class File(ABC):
    """
    Represents a file in the file system.
    """

    def __init__(self, name: str, permissions: int, owner: str):
        assert 0 <= permissions <= 0o777, "Invalid permissions..."
        self.name = name
        self.permissions = permissions
        self.owner = owner

    @abstractmethod
    def map_content(self, function: Callable[[str], str]) -> "File":
        """
        Maps over the content of regular files, and just traverses the rest of the file system.
        Does not follow links. The function is applied to the content of the file.
        """
        pass

    @abstractmethod
    def map_files(self, function: Callable[["File"], None]):
        """
        Maps over all the files and directories in the file system. Does not follow links.
        Changes are done in-place.
        """
        pass


class RegularFile(File):
    """
    Represents a regular file in the file system, which is just a file with some content inside.
    """

    def __init__(self, name: str, permissions: int, owner: str, content: str):
        super().__init__(name, permissions, owner)
        self.content = content

    def map_content(self, function: Callable[[str], str]) -> "RegularFile":
        return RegularFile(self.name, self.permissions, self.owner, function(self.content))

    def map_files(self, function: Callable[["File"], None]):
        function(self)

    def __eq__(self, other):
        return self.name == other.name and self.permissions == other.permissions and self.owner == other.owner and self.content == other.content


class Directory(File):
    """
    Represents a directory in the file system, which is basically a file with a list of files.
    """

    def __init__(self, name: str, permissions: int, owner: str, files: List[File]):
        super().__init__(name, permissions, owner)
        self.files = files

    def map_content(self, function: Callable[[str], str]) -> "Directory":
        return Directory(self.name, self.permissions, self.owner, [f.map_content(function) for f in self.files])

    def map_files(self, function: Callable[["File"], None]):
        function(self)
        for f in self.files:
            f.map_files(function)

    def __eq__(self, other):
        return self.name == other.name and self.permissions == other.permissions and self.owner == other.owner and all(
            a == b for a, b in zip(self.files, other.files))

Tests:
### START TESTS ###
if True: # pragma: no cover
    regular_file = RegularFile("example.txt", 0o644, "user1", "Hello, world!")
    assert regular_file.name == "example.txt"
    assert regular_file.permissions == 0o644
    assert regular_file.owner == "user1"
    assert regular_file.content == "Hello, world!"

    try:
        invalid_file = RegularFile(
            "badfile.txt", 0o1000, "user2", "This should fail")
    except:
        pass
    else:
        assert False, "Expected an AssertionError for invalid permissions"


    assert regular_file.owner == "user1"

    transformed_file = regular_file.map_content(lambda content: content.upper())
    assert transformed_file.content == "HELLO, WORLD!"
    assert transformed_file.name == "example.txt"
    assert transformed_file.permissions == 0o644


    regular_file = RegularFile("example.txt", 0o644, "user1", "Hello, world!")
    regular_file_exp1 = RegularFile(
        "example.txt", 0o644, "user1", "HELLO, WORLD!")

    assert regular_file.map_content(
        lambda content: content.upper()) == regular_file_exp1

    d1 = Directory("user1", 0o700, "user1", [
        regular_file,
        RegularFile("notes.txt", 0o600, "user1", "Some notes"),
        RegularFile("todo.txt", 0o600, "user1", "Some tasks"),
    ])

    d1_exp = Directory("user1", 0o700, "user1", [
        regular_file_exp1,
        RegularFile("notes.txt", 0o600, "user1", "SOME NOTES"),
        RegularFile("todo.txt", 0o600, "user1", "SOME TASKS"),
    ])

    assert d1.map_content(lambda content: content.upper()) == d1_exp


    d2 = Directory("user2", 0o700, "user2", [
        d1,
        RegularFile("stuff.txt", 0o600, "user2", "Some stuff"),
    ])

    d2_exp = Directory("user2", 0o700, "user2", [
        d1_exp,
        RegularFile("stuff.txt", 0o600, "user2", "SOME STUFF"),
    ])

    assert d2.map_content(lambda content: content.upper()) == d2_exp


    fs = Directory("root", 0o755, "user1", [
        Directory("home", 0o755, "user1", [
            d2,
        ]),
    ])

    fs_exp = Directory("root", 0o755, "user1", [
        Directory("home", 0o755, "user1", [
            d2_exp,
        ]),
    ])

    assert fs.map_content(lambda content: content.upper()) == fs_exp


    regular_file_exp2 = RegularFile(
        "EXAMPLE.TXT", 0o644, "user1", "Hello, world!")


    def upper_name(file: File):
        file.name = file.name.upper()


    new_regular_file = RegularFile("example.txt", 0o644, "user1", "Hello, world!")
    new_regular_file.map_files(upper_name)
    assert new_regular_file == regular_file_exp2

    new_d1 = Directory("user1", 0o700, "user1", [
        new_regular_file,
        RegularFile("notes.txt", 0o600, "user1", "Some notes"),
        RegularFile("todo.txt", 0o600, "user1", "Some tasks"),
    ])

    new_d1_exp = Directory("USER1", 0o700, "user1", [
        regular_file_exp2,
        RegularFile("NOTES.TXT", 0o600, "user1", "Some notes"),
        RegularFile("TODO.TXT", 0o600, "user1", "Some tasks"),
    ])

    new_d1.map_files(upper_name)
    assert new_d1 == new_d1_exp

    new_d2 = Directory("user2", 0o700, "user2", [
        Directory("home", 0o755, "user2", [
            Directory("user1", 0o700, "user1", [
                new_regular_file,
                RegularFile("notes.txt", 0o600, "user1", "Some notes"),
                RegularFile("todo.txt", 0o600, "user1", "Some tasks"),
            ]),
        ]),
        RegularFile("stuff.txt", 0o600, "user2", "Some stuff"),
    ])

    new_d2_exp = Directory("USER2", 0o700, "user2", [
        Directory("HOME", 0o755, "user2", [
            Directory("USER1", 0o700, "user1", [
                regular_file_exp2,
                RegularFile("NOTES.TXT", 0o600, "user1", "Some notes"),
                RegularFile("TODO.TXT", 0o600, "user1", "Some tasks"),
            ]),
        ]),
        RegularFile("STUFF.TXT", 0o600, "user2", "Some stuff"),
    ])

    new_d2.map_files(upper_name)
    assert new_d2 == new_d2_exp