Allow `# Ignore copy`
Author: ydshieh
Pull request: yes

# What does this PR do?

Allow `# ignore copied` as suggested in https://github.com/huggingface/transformers/pull/26713#discussion_r1354625364

The runtime of `check_copies` is the same as before.

See the comment below for an example.

Comments:
The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/transformers/pr_27328). All of your documentation changes will be reflected on that endpoint.

## observed code (where there is `#Copied from`)
#### (There are 2 `# ignore copied` blocks: one is not in the target code, another one is but having different content)

```python
----------------------------------------
class RobertaBertDummyModel:
----------------------------------------
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
----------------------------------------

----------------------------------------
    # ignore copied
    def only_in_roberta_to_be_ignored(self, c):
        return 3
----------------------------------------

----------------------------------------
    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1
----------------------------------------

----------------------------------------
    def only_in_roberta_not_ignored(self, c):
        return 2
----------------------------------------

----------------------------------------
    def existing_common(self, c):
        return 4
----------------------------------------

----------------------------------------
    def existing_diff_not_ignored(self, c):
        return 5
----------------------------------------

----------------------------------------
    # ignore copied
    def existing_diff_to_be_ignored(self, c):
        return 6
----------------------------------------
```

## target code (where is specified by `#Copied from`)
```python
----------------------------------------
class BertDummyModel:
----------------------------------------
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
----------------------------------------

----------------------------------------
    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1
----------------------------------------

----------------------------------------
    def only_in_bert(self, c):
        return 7
----------------------------------------

----------------------------------------
    def existing_common(self, c):
        return 4
----------------------------------------

----------------------------------------
    def existing_diff_not_ignored(self, c):
        return 8
----------------------------------------

----------------------------------------
    def existing_diff_to_be_ignored(self, c):
        return 9
----------------------------------------
```

<details>
  <summary>Intermediate results</summary>

## replaced target code (by patterns)
#### (just `BertDummyModel` -> `RobertaBertDummyModel`)
```python
----------------------------------------
class RobertaBertDummyModel:
----------------------------------------
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
----------------------------------------

----------------------------------------
    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1
----------------------------------------

----------------------------------------
    def only_in_bert(self, c):
        return 7
----------------------------------------

----------------------------------------
    def existing_common(self, c):
        return 4
----------------------------------------

----------------------------------------
    def existing_diff_not_ignored(self, c):
        return 8
----------------------------------------

----------------------------------------
    def existing_diff_to_be_ignored(self, c):
        return 9
----------------------------------------
```



## target code taking into account `# ignore copied` from observed code
#### (target code use to write before blackify)
#### (the 2 `# ignore copied` blocks in the observed code are now in the target code)
```python
----------------------------------------
class RobertaBertDummyModel:
----------------------------------------
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
----------------------------------------

----------------------------------------
    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1
----------------------------------------

----------------------------------------
    def only_in_bert(self, c):
        return 7
----------------------------------------

----------------------------------------
    def existing_common(self, c):
        return 4
----------------------------------------

----------------------------------------
    def existing_diff_not_ignored(self, c):
        return 8
----------------------------------------

----------------------------------------
    # ignore copied
    def existing_diff_to_be_ignored(self, c):
        return 6
----------------------------------------
    # ignore copied
    def only_in_roberta_to_be_ignored(self, c):
        return 3
----------------------------------------
```

## target code use to compare (remove `# ignore copied` and empty blocks)

```python
class RobertaBertDummyModel:
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1
    def only_in_bert(self, c):
        return 7
    def existing_common(self, c):
        return 4
    def existing_diff_not_ignored(self, c):
        return 8
```

## observed code use to compare (remove `# ignore copied` and empty blocks)

```python
class RobertaBertDummyModel:
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1
    def only_in_roberta_not_ignored(self, c):
        return 2
    def existing_common(self, c):
        return 4
    def existing_diff_not_ignored(self, c):
        return 5
```
</details>

## target code use to overwrite

```python
class RobertaBertDummyModel:
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b

    # Copied from transformers.models.dummy_gpt2.modeling_dummy_gpt2.GPT2DummyModel.forward
    def forward(self, c):
        return 1

    def only_in_bert(self, c):
        return 7

    def existing_common(self, c):
        return 4

    def existing_diff_not_ignored(self, c):
        return 8

    # ignore copied
    def existing_diff_to_be_ignored(self, c):
        return 6

    # ignore copied
    def only_in_roberta_to_be_ignored(self, c):
        return 3
```

Rebased on `main` 🎉 

I'll review tomorrow! 

> I'll review tomorrow!

I have a meeting with Lysandre to explain this PR. We can discuss all together, so might be easier for the review.

Following the discussion in our last meeting, I tried to implemented each line with a certain level of indent (that is not in an inner function or class) as a single block.

However, I realized this is too error prone, like

```python
class RobertaTokenizationTest:
    tokenizer_class = RobertaTokenizer
    rust_tokenizer_class = RobertaTokenizerFast
    test_rust_tokenizer = True
    from_pretrained_kwargs = {"cls_token": "
"}

    def setUp(self):
        super().setUp()
```
vs
```python
class LongformerTokenizationTest
    # ignore copied
    tokenizer_class = LongformerTokenizer
    test_slow_tokenizer = True
    rust_tokenizer_class = LongformerTokenizerFast
    test_rust_tokenizer = True

    def setUp(self):
        super().setUp()
```

Those blocks (each line before the `setUp`) have **no well defined name**, and there is no reliable way to map the blocks between the source and target files. For this simple case, we might say just arrange a bit the files and it would work. But it's just a guess that happens to work in a special case.

I suggest to treat the block before any inner class/func/method **as a single block**. This is sill not 100% theoretically reliable, but it will work until users are writing code in strange format.

WDYT?

> I'll review again after the last changes!

It's not ready, as we need to agree what to do regarding our last discussion offline.

@ArthurZucker Updated the tests [here](https://github.com/huggingface/transformers/pull/27328/commits/3a5b22075cb126cc0e16c8382bc00a858e21e91d) as suggested

Feel free to merge!