code: def test_span_group_copy(doc):
      span_group = doc.spans["SPANS"]
      clone = span_group.copy()
      assert clone != span_group
      assert clone.name == span_group.name
      assert clone.attrs == span_group.attrs
      assert len(clone) == len(span_group)
      assert list(span_group) == list(clone)
      clone.name = "new_name"
      clone.attrs["key"] = "new_value"
      clone.append(Span(doc, 0, 6, "LABEL"))
      assert clone.name != span_group.name
      assert clone.attrs != span_group.attrs
      assert span_group.attrs["key"] == "value"
      assert list(span_group) != list(clone)
  
      # can't copy if the character offsets don't align to tokens
      doc2 = Doc(doc.vocab, words=[t.text + "x" for t in doc])
      with pytest.raises(ValueError):
          span_group.copy(doc=doc2)
  
      # can copy with valid character offsets despite different tokenization
      doc3 = doc.copy()
      with doc3.retokenize() as retokenizer:
          retokenizer.merge(doc3[0:2])
          retokenizer.merge(doc3[3:6])
      span_group = SpanGroup(doc, spans=[doc[0:6], doc[3:6]])
      for span1, span2 in zip(span_group, span_group.copy(doc=doc3)):
          assert span1.start_char == span2.start_char
          assert span1.end_char == span2.end_char
  
file_path: spaCy/spacy/tests/doc/test_span_group.py
imports: from random import Random
  from typing import List
  import pytest
  from spacy.matcher import Matcher
  from spacy.tokens import Doc, Span, SpanGroup
  from spacy.util import filter_spans
label_desc: Use image node_id 4 for calling a global function with example usage: test_span_group_copy(doc) without return types