# ____________________________________________________________________________________
#
# Pyomo: Python Optimization Modeling Objects
# Copyright (c) 2008-2026 National Technology and Engineering Solutions of Sandia, LLC
# Under the terms of Contract DE-NA0003525 with National Technology and Engineering
# Solutions of Sandia, LLC, the U.S. Government retains certain rights in this
# software.  This software is distributed under the 3-clause BSD License.
# ____________________________________________________________________________________
#
# Unit Tests for ComponentUID
#
import pickle
from collections import namedtuple
from datetime import datetime
from io import StringIO

import pyomo.common.unittest as unittest
from pyomo.environ import (
    ConcreteModel,
    Block,
    Var,
    Set,
    Param,
    Constraint,
    Any,
    ComponentUID,
    Reference,
)
from pyomo.core.base.indexed_component import IndexedComponent
from pyomo.core.base.indexed_component_slice import IndexedComponent_slice
from pyomo.common.log import LoggingIntercept

_star = slice(None)

_Foo = namedtuple('_Foo', ['x', 'yy'])


class TestComponentUID(unittest.TestCase):
    def setUp(self):
        self.m = ConcreteModel()
        m = self.m
        m.a = Param()
        m.s = Set(initialize=[1, '2', 3])
        m.b = Block(m.s, m.s)
        m.b[1, 1].c = Block()
        m.b[1, '2'].c = Block()
        m.b[1, '2'].c.a = Param(m.s, initialize=3, mutable=True)

    def tearDown(self):
        self.m = None

    def test_genFromComponent_simple(self):
        cuid = ComponentUID(self.m.a)
        self.assertEqual(cuid._cids, (('a', tuple()),))

    def test_genFromComponent_nested(self):
        cuid = ComponentUID(self.m.b[1, '2'].c.a[3])
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (3,))))

    def test_genFromComponent_indexed(self):
        cuid = ComponentUID(self.m.b[1, '2'].c.a)
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', ())))

    def test_genFromComponent_nameBuffer(self):
        buf = {}
        cuid = ComponentUID(self.m.b[1, '2'].c.a, cuid_buffer=buf)
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', ())))
        self.assertEqual(len(buf), 9)
        for s1 in self.m.s:
            for s2 in self.m.s:
                _id = id(self.m.b[s1, s2])
                self.assertIn(_id, buf)
                self.assertEqual(buf[_id], ('b', (s1, s2)))

    def test_genFromComponent_context(self):
        cuid = ComponentUID(self.m.b[1, '2'].c.a, context=self.m.b[1, '2'])
        self.assertEqual(cuid._cids, (('c', tuple()), ('a', ())))
        with self.assertRaisesRegex(
            ValueError, r"Context 'b\[1,'2'\]' does not apply to component 's'"
        ):
            ComponentUID(self.m.s, context=self.m.b[1, '2'])
        with self.assertRaisesRegex(ValueError, "Context is not allowed"):
            ComponentUID("b[1,2].c.a[2]", context=self.m.b[1, '2'])

    def test_parseFromString(self):
        cuid = ComponentUID('b[1,2].c.a[2]')
        self.assertEqual(cuid._cids, (('b', (1, 2)), ('c', tuple()), ('a', (2,))))

    def test_parseFromString_singleQuote(self):
        cuid = ComponentUID('b[1,\'2\'].c.a[2]')
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (2,))))

    def test_parseFromString_doubleQuote(self):
        cuid = ComponentUID('b[1,\"2\"].c.a[2]')
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (2,))))

    def test_parseFromString_typeID(self):
        cuid = ComponentUID('b[#1,$2].c.a[2]')
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (2,))))

    def test_parseFromString_wildcard_1(self):
        cuid = ComponentUID('b[**].c.a[*]')
        self.assertEqual(
            cuid._cids, (('b', (Ellipsis,)), ('c', tuple()), ('a', (_star,)))
        )

    def test_parseFromString_wildcard_2(self):
        cuid = ComponentUID('b[*,*].c.a[*]')
        self.assertEqual(
            cuid._cids, (('b', (_star, _star)), ('c', tuple()), ('a', (_star,)))
        )

    def test_parseFromString_spaces(self):
        cuid = ComponentUID('x[a b,c d]')
        self.assertEqual(cuid._cids, (('x', ('a b', 'c d')),))

        cuid = ComponentUID("x['a b',\"c d\"]")
        self.assertEqual(cuid._cids, (('x', ('a b', 'c d')),))

        cuid = ComponentUID('x[a b, c d]')
        self.assertEqual(cuid._cids, (('x', ('a b', 'c d')),))

        cuid = ComponentUID("x[ a b , 'c d' ]")
        self.assertEqual(cuid._cids, (('x', ('a b', 'c d')),))

    def test_parseFromRepr1(self):
        cuid = ComponentUID('b:1,2.c.a:2')
        self.assertEqual(cuid._cids, (('b', (1, 2)), ('c', tuple()), ('a', (2,))))

    def test_parseFromRepr1_singleQuote(self):
        cuid = ComponentUID('b:1,\'2\'.c.a:2')
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (2,))))

    def test_parseFromRepr1_doubleQuote(self):
        cuid = ComponentUID('b:1,\"2\".c.a:2')
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (2,))))

    def test_parseFromRepr1_typeID(self):
        cuid = ComponentUID('b:#1,$2.c.a:2')
        self.assertEqual(cuid._cids, (('b', (1, '2')), ('c', tuple()), ('a', (2,))))

    def test_parseFromRepr1_wildcard_1(self):
        cuid = ComponentUID('b:**.c.a:*')
        self.assertEqual(
            cuid._cids, (('b', (Ellipsis,)), ('c', tuple()), ('a', (_star,)))
        )

    def test_parseFromRepr1_wildcard_2(self):
        cuid = ComponentUID('b:*,*.c.a:*')
        self.assertEqual(
            cuid._cids, (('b', (_star, _star)), ('c', tuple()), ('a', (_star,)))
        )

    def test_parseFromRepr2_lexError(self):
        cuid = ComponentUID('')  # Bogus instance to access parser
        with self.assertRaisesRegex(IOError, "ERROR: Token ':' Line 1 Column 1"):
            list(cuid._parse_cuid_v2(':'))
        with self.assertRaisesRegex(IOError, "ERROR: Token '\n].b:' Line 1 Column 3"):
            list(cuid._parse_cuid_v2('a[\n].b:'))

    def test_escapeChars(self):
        ref = r"b['a\n.b\\'].x"
        cuid = ComponentUID(ref)
        self.assertEqual(cuid._cids, (('b', ('a\n.b\\',)), ('x', tuple())))

        m = ConcreteModel()
        m.b = Block(['a\n.b\\'])
        m.b['a\n.b\\'].x = x = Var()
        self.assertTrue(cuid.matches(x))
        self.assertEqual(repr(ComponentUID(x)), ref)
        self.assertEqual(str(ComponentUID(x)), r"b['a\n.b\\'].x")

    def test_nonIntNumber(self):
        inf = float('inf')
        m = ConcreteModel()
        m.b = Block([inf, 'inf'])

        m.b[inf].x = x = Var()
        ref = r"b[inf].x"

        cuid = ComponentUID(x)
        self.assertEqual(cuid._cids, (('b', (inf,)), ('x', tuple())))

        self.assertTrue(cuid.matches(x))
        self.assertEqual(repr(ComponentUID(x)), ref)
        self.assertEqual(str(ComponentUID(x)), ref)

        cuid = ComponentUID(ref)
        self.assertEqual(cuid._cids, (('b', (inf,)), ('x', tuple())))

        self.assertTrue(cuid.matches(x))
        self.assertEqual(repr(ComponentUID(x)), ref)
        self.assertEqual(str(ComponentUID(x)), ref)

        ref = r"b:#inf.x"
        cuid = ComponentUID(ref)
        self.assertEqual(cuid._cids, (('b', (inf,)), ('x', tuple())))

        self.assertTrue(cuid.matches(x))
        self.assertEqual(ComponentUID(x).get_repr(1), ref)
        self.assertEqual(str(ComponentUID(x)), r"b[inf].x")

        #
        m.b['inf'].x = x = Var()
        ref = r"b['inf'].x"
        #

        cuid = ComponentUID(x)
        self.assertEqual(cuid._cids, (('b', ('inf',)), ('x', tuple())))

        self.assertTrue(cuid.matches(x))
        self.assertEqual(repr(ComponentUID(x)), ref)
        self.assertEqual(str(ComponentUID(x)), ref)

        cuid = ComponentUID(ref)
        self.assertEqual(cuid._cids, (('b', ('inf',)), ('x', tuple())))

        self.assertTrue(cuid.matches(x))
        self.assertEqual(repr(ComponentUID(x)), ref)
        self.assertEqual(str(ComponentUID(x)), ref)

        ref = r"b:$inf.x"
        cuid = ComponentUID(ref)
        self.assertEqual(cuid._cids, (('b', ('inf',)), ('x', tuple())))

        self.assertTrue(cuid.matches(x))
        self.assertEqual(ComponentUID(x).get_repr(1), ref)
        self.assertEqual(str(ComponentUID(x)), r"b['inf'].x")

    def test_find_component_deprecated(self):
        ref = self.m.b[1, '2'].c.a[3]
        cuid = ComponentUID(ref)
        DEP_OUT = StringIO()
        with LoggingIntercept(DEP_OUT, 'pyomo.core'):
            self.assertTrue(cuid.find_component(self.m) is ref)
        self.assertIn(
            'ComponentUID.find_component() is deprecated.', DEP_OUT.getvalue()
        )

    def test_find_explicit_exists(self):
        ref = self.m.b[1, '2'].c.a[3]
        cuid = ComponentUID(ref)
        self.assertTrue(cuid.find_component_on(self.m) is ref)

    def test_find_component_exists_1(self):
        ref = self.m.b[1, '2'].c.a
        cuid = ComponentUID(ref)
        self.assertTrue(cuid.find_component_on(self.m) is ref)

    def test_find_wildcard(self):
        cuid = ComponentUID('b:1,$2.c.a:*')
        comp = cuid.find_component_on(self.m)
        self.assertIs(comp.ctype, Param)
        cList = list(comp.values())
        self.assertEqual(len(cList), 3)
        self.assertEqual(cList, list(self.m.b[1, '2'].c.a[:]))

        cuid = ComponentUID('b[*,*]')
        comp = cuid.find_component_on(self.m)
        self.assertIs(comp.ctype, Block)
        cList = list(comp.values())
        self.assertEqual(len(cList), 9)
        self.assertEqual(cList, list(self.m.b.values()))

    def test_find_wildcard_partial_exists(self):
        # proper Reference: to ComponentData
        cuid = ComponentUID('b[*,*].c.a[**]')
        comp = cuid.find_component_on(self.m)
        self.assertIs(comp.ctype, Param)
        cList = list(comp.values())
        self.assertEqual(len(cList), 3)
        self.assertEqual(cList, list(self.m.b[1, '2'].c.a[:]))

        # improper Reference: to IndexedComponent
        cuid = ComponentUID('b[*,*].c.a')
        comp = cuid.find_component_on(self.m)
        self.assertIs(comp.ctype, IndexedComponent)
        cList = list(comp.values())
        self.assertEqual(len(cList), 1)
        self.assertIs(cList[0], self.m.b[1, '2'].c.a)

    def test_find_wildcard_not_exists(self):
        cuid = ComponentUID('b[*,*].c.x')
        self.assertIsNone(cuid.find_component_on(self.m))

    # def test_find_implicit_exists(self):
    #     cuid = ComponentUID('b:1,2.c.a:3')
    #     self.assertTrue(cuid.find_component_on(self.m) is
    #                     self.m.b[1,'2'].c.a[3])

    def test_find_implicit_notExists_1(self):
        cuid = ComponentUID('b:1,2.c.a:4')
        self.assertTrue(cuid.find_component_on(self.m) is None)

    def test_find_implicit_notExists_2(self):
        cuid = ComponentUID('b:1,1.c.a:3')
        self.assertTrue(cuid.find_component_on(self.m) is None)

    def test_find_explicit_notExists_1(self):
        cuid = ComponentUID('b:1,2.c.a:$3')
        self.assertTrue(cuid.find_component_on(self.m) is None)

    def test_find_explicit_notExists_2(self):
        cuid = ComponentUID('b:$1,2.c.a:3')
        self.assertTrue(cuid.find_component_on(self.m) is None)

    def test_printers_1(self):
        cuid = ComponentUID(self.m.b[1, '2'].c.a[3])
        s = "b[1,'2'].c.a[3]"
        r1 = "b:#1,$2.c.a:#3"
        r2 = "b[1,'2'].c.a[3]"
        self.assertEqual(str(cuid), s)
        self.assertEqual(repr(cuid), r2)
        self.assertEqual(cuid.get_repr(1), r1)
        self.assertEqual(cuid.get_repr(2), r2)
        with self.assertRaisesRegex(
            ValueError, "Invalid repr version '3'; expected 1 or 2"
        ):
            cuid.get_repr(3)

    def test_printers_2(self):
        cuid = ComponentUID('b:$1,2.c.a:#3')
        s = "b['1',2].c.a[3]"
        r1 = "b:$1,#2.c.a:#3"
        r2 = "b['1',2].c.a[3]"
        self.assertEqual(str(cuid), s)
        self.assertEqual(repr(cuid), r2)
        self.assertEqual(cuid.get_repr(1), r1)
        self.assertEqual(cuid.get_repr(2), r2)

    def test_printers_3(self):
        cuid = ComponentUID('b:**.c.a:*')
        s = 'b[**].c.a[*]'
        r1 = "b:**.c.a:*"
        r2 = "b[**].c.a[*]"
        self.assertEqual(str(cuid), s)
        self.assertEqual(repr(cuid), r2)
        self.assertEqual(cuid.get_repr(1), r1)
        self.assertEqual(cuid.get_repr(2), r2)

    def test_printers_4(self):
        cuid = ComponentUID('b:*,*.c.a:**')
        s = 'b[*,*].c.a[**]'
        r1 = "b:*,*.c.a:**"
        r2 = "b[*,*].c.a[**]"
        self.assertEqual(str(cuid), s)
        self.assertEqual(repr(cuid), r2)
        self.assertEqual(cuid.get_repr(1), r1)
        self.assertEqual(cuid.get_repr(2), r2)

    def test_matches_explicit(self):
        cuid = ComponentUID(self.m.b[1, '2'].c.a[3])
        self.assertTrue(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a['2']))

    # def test_matches_implicit(self):
    #     cuid = ComponentUID('b:1,2.c.a:3')
    #     self.assertTrue(cuid.matches(self.m.b[1,'2'].c.a[3]))
    #     self.assertFalse(cuid.matches(self.m.b[1,'2'].c.a['2']))

    def test_matches_explicit_1(self):
        cuid = ComponentUID('b:#1,$2.c.a:$3')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_explicit_2(self):
        cuid = ComponentUID('b:#1,#2.c.a:#3')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_wildcard_1(self):
        cuid = ComponentUID('b:**.c.a:*')
        self.assertTrue(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertTrue(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_wildcard_2(self):
        cuid = ComponentUID('b:*,*.c.a:**')
        self.assertTrue(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertTrue(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_wildcard_3(self):
        cuid = ComponentUID('b:*,*.c.a:*,*')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_mismatch_name(self):
        cuid = ComponentUID('b:*,*.d')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))

    def test_matches_mismatch_1(self):
        cuid = ComponentUID('b:*,*.c.a:*')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))

    def test_matches_mismatch_2(self):
        cuid = ComponentUID('b:*,*.c')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_mismatch_3(self):
        cuid = ComponentUID('b:*,*,*.c.a:*')
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a[3]))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c.a['2']))

    def test_matches_ellipsis1(self):
        cuid = ComponentUID('b[**,1].c')
        self.assertTrue(cuid.matches(self.m.b[1, 1].c))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))

    def test_matches_ellipsis2(self):
        cuid = ComponentUID('b[**,1,1].c')
        self.assertTrue(cuid.matches(self.m.b[1, 1].c))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))

    def test_matches_ellipsis3(self):
        cuid = ComponentUID('b[**,1,1,3].c')
        self.assertFalse(cuid.matches(self.m.b[1, 1].c))
        self.assertFalse(cuid.matches(self.m.b[1, '2'].c))

    def test_matches_ellipsis4(self):
        cuid = ComponentUID('b[**,1,*].c')
        self.assertTrue(cuid.matches(self.m.b[1, 1].c))
        self.assertTrue(cuid.matches(self.m.b[1, '2'].c))

    def test_list_components_done_1(self):
        cuid = ComponentUID('b:*,*,*.c.a:*')
        ref = []
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_list_components_done_2(self):
        cuid = ComponentUID('b:*,*.c:#1.a:*')
        ref = []
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_list_components_scalar(self):
        cuid = ComponentUID('b:1,$2.c.a:3')
        ref = [str(ComponentUID(self.m.b[1, '2'].c.a[3]))]
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_list_components_wildcard_1(self):
        cuid = ComponentUID('b:**.c.a:3')
        ref = [str(ComponentUID(self.m.b[1, '2'].c.a[3]))]
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_list_components_wildcard_2(self):
        cuid = ComponentUID('b:*,*.c.a:*')
        ref = [
            str(ComponentUID(self.m.b[1, '2'].c.a[1])),
            str(ComponentUID(self.m.b[1, '2'].c.a['2'])),
            str(ComponentUID(self.m.b[1, '2'].c.a[3])),
        ]
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_list_components_wildcard_3(self):
        cuid = ComponentUID('b:1,*.c')
        ref = [
            str(ComponentUID(self.m.b[1, 1].c)),
            str(ComponentUID(self.m.b[1, '2'].c)),
        ]
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_list_components_wildcard_4(self):
        cuid = ComponentUID('b:1,*')
        ref = [
            str(ComponentUID(self.m.b[1, 1])),
            str(ComponentUID(self.m.b[1, '2'])),
            str(ComponentUID(self.m.b[1, 3])),
        ]
        cList = [str(ComponentUID(x)) for x in cuid.list_components(self.m)]
        self.assertEqual(sorted(cList), sorted(ref))

    def test_in_container(self):
        a = ComponentUID('foo.bar[*]')
        b = ComponentUID('baz')
        c = ComponentUID('baz.bar')
        D = {a: 1, b: 2}

        self.assertTrue(a in D)
        self.assertTrue(b in D)
        self.assertFalse(c in D)

        # Verify that hashing is not being done by id()
        self.assertTrue(ComponentUID('foo.bar[*]') in D)
        self.assertTrue(ComponentUID('baz') in D)

    def test_comparisons(self):
        a = ComponentUID('foo.x[*]')
        b = ComponentUID('baz')

        self.assertFalse(a < b)
        self.assertFalse(a <= b)
        self.assertTrue(a > b)
        self.assertTrue(a >= b)
        self.assertFalse(a == b)
        self.assertTrue(a != b)

        self.assertTrue(b < a)
        self.assertTrue(b <= a)
        self.assertFalse(b > a)
        self.assertFalse(b >= a)
        self.assertFalse(b == a)
        self.assertTrue(b != a)

        self.assertFalse(a < ComponentUID('baz'))
        self.assertFalse(a <= ComponentUID('baz'))
        self.assertTrue(a > ComponentUID('baz'))
        self.assertTrue(a >= ComponentUID('baz'))
        self.assertFalse(a == ComponentUID('baz'))
        self.assertTrue(a != ComponentUID('baz'))

        self.assertTrue(ComponentUID('baz') < a)
        self.assertTrue(ComponentUID('baz') <= a)
        self.assertFalse(ComponentUID('baz') > a)
        self.assertFalse(ComponentUID('baz') >= a)
        self.assertFalse(ComponentUID('baz') == a)
        self.assertTrue(ComponentUID('baz') != a)

        self.assertFalse(b < b)
        self.assertTrue(b <= b)
        self.assertFalse(b > b)
        self.assertTrue(b >= b)
        self.assertTrue(b == b)
        self.assertFalse(b != b)

        self.assertFalse(ComponentUID('baz') < b)
        self.assertTrue(ComponentUID('baz') <= b)
        self.assertFalse(ComponentUID('baz') > b)
        self.assertTrue(ComponentUID('baz') >= b)
        self.assertTrue(ComponentUID('baz') == b)
        self.assertFalse(ComponentUID('baz') != b)

    def test_comparisons_lt(self):
        a = ComponentUID('foo.x[*]')
        a1 = ComponentUID('foo.x[1]')
        a2 = ComponentUID('foo.x[2]')
        aa = ComponentUID("foo.x['a']")
        a11 = ComponentUID('foo.x[1,1]')
        ae = ComponentUID('foo.x[**]')
        self.assertTrue(a < ae)
        self.assertTrue(a1 < ae)
        self.assertTrue(a1 < a)
        self.assertTrue(a1 < a2)
        self.assertTrue(a1 < aa)
        self.assertTrue(a1 < a11)
        self.assertTrue(a11 < a2)
        self.assertFalse(ae < a)
        self.assertFalse(ae < a1)
        self.assertFalse(a < a1)
        self.assertFalse(a2 < a1)
        self.assertFalse(aa < a1)
        self.assertFalse(a11 < a1)
        self.assertFalse(a2 < a11)

        x = ComponentUID('foo.x')
        xy = ComponentUID('foo.x.y')
        self.assertTrue(x < xy)
        self.assertFalse(xy < x)

        with self.assertRaisesRegex(
            TypeError, "'<' not supported between instances of 'ComponentUID' and 'int'"
        ):
            a < 5

    def test_comparisons_eq(self):
        a = ComponentUID('foo.x[*]')
        a1 = ComponentUID('foo.x[1]')
        b = ComponentUID('foo.x[*]')
        self.assertEqual(a, b)
        self.assertNotEqual(a, a1)
        self.assertNotEqual(a, 5)

    def test_generate_cuid_string_map(self):
        model = Block(concrete=True)
        model.x = Var()
        model.y = Var([1, 2])
        model.V = Var([('a', 'b'), (1, '2'), (3, 4)])
        model.b = Block(concrete=True)
        model.b.z = Var([1, '2'])
        setattr(model.b, '.H', Var(['a', 2]))
        model.B = Block(['a', 2], concrete=True)
        setattr(model.B['a'], '.k', Var())
        model.B[2].b = Block()
        model.B[2].b.x = Var()
        model.add_component('c tuple', Constraint(Any))
        model.component('c tuple')[(1,)] = model.x >= 0

        cuids = (
            ComponentUID.generate_cuid_string_map(model, repr_version=1),
            ComponentUID.generate_cuid_string_map(model),
        )
        self.assertEqual(len(cuids[0]), 24)
        self.assertEqual(len(cuids[1]), 24)
        for obj in [
            model,
            model.x,
            model.y,
            model.y[1],
            model.y[2],
            model.V,
            model.V['a', 'b'],
            model.V[1, '2'],
            model.V[3, 4],
            model.b,
            model.b.z,
            model.b.z[1],
            model.b.z['2'],
            getattr(model.b, '.H'),
            getattr(model.b, '.H')['a'],
            getattr(model.b, '.H')[2],
            model.B,
            model.B['a'],
            getattr(model.B['a'], '.k'),
            model.B[2],
            model.B[2].b,
            model.B[2].b.x,
            model.component('c tuple')[(1,)],
        ]:
            self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj])
            self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj])

        cuids = (
            ComponentUID.generate_cuid_string_map(
                model, descend_into=False, repr_version=1
            ),
            ComponentUID.generate_cuid_string_map(model, descend_into=False),
        )
        self.assertEqual(len(cuids[0]), 15)
        self.assertEqual(len(cuids[1]), 15)
        for obj in [
            model,
            model.x,
            model.y,
            model.y[1],
            model.y[2],
            model.V,
            model.V['a', 'b'],
            model.V[1, '2'],
            model.V[3, 4],
            model.b,
            model.B,
            model.B['a'],
            model.B[2],
            model.component('c tuple')[(1,)],
        ]:
            self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj])
            self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj])

        cuids = (
            ComponentUID.generate_cuid_string_map(model, ctype=Var, repr_version=1),
            ComponentUID.generate_cuid_string_map(model, ctype=Var),
        )
        self.assertEqual(len(cuids[0]), 22)
        self.assertEqual(len(cuids[1]), 22)
        for obj in [
            model,
            model.x,
            model.y,
            model.y[1],
            model.y[2],
            model.V,
            model.V['a', 'b'],
            model.V[1, '2'],
            model.V[3, 4],
            model.b,
            model.b.z,
            model.b.z[1],
            model.b.z['2'],
            getattr(model.b, '.H'),
            getattr(model.b, '.H')['a'],
            getattr(model.b, '.H')[2],
            model.B,
            model.B['a'],
            getattr(model.B['a'], '.k'),
            model.B[2],
            model.B[2].b,
            model.B[2].b.x,
        ]:
            self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj])
            self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj])

        cuids = (
            ComponentUID.generate_cuid_string_map(
                model, ctype=Var, descend_into=False, repr_version=1
            ),
            ComponentUID.generate_cuid_string_map(model, ctype=Var, descend_into=False),
        )
        self.assertEqual(len(cuids[0]), 9)
        self.assertEqual(len(cuids[1]), 9)
        for obj in [
            model,
            model.x,
            model.y,
            model.y[1],
            model.y[2],
            model.V,
            model.V['a', 'b'],
            model.V[1, '2'],
            model.V[3, 4],
        ]:
            self.assertEqual(ComponentUID(obj).get_repr(1), cuids[0][obj])
            self.assertEqual(repr(ComponentUID(obj)), cuids[1][obj])

    def test_pickle(self):
        a = ComponentUID("b[1,'2'].c")
        b = pickle.loads(pickle.dumps(a))
        self.assertIsNot(a, b)
        self.assertEqual(a, b)

        a = ComponentUID("b[1,*].c")
        b = pickle.loads(pickle.dumps(a))
        self.assertIsNot(a, b)
        self.assertEqual(a, b)

        a = ComponentUID("b[**,*].c")
        b = pickle.loads(pickle.dumps(a))
        self.assertIsNot(a, b)
        self.assertEqual(a, b)

    def test_findComponentOn_nestedTuples(self):
        # Tests for #1069
        m = ConcreteModel()
        m.x = Var()
        m.c = Constraint(Any)
        m.c[0] = m.x >= 0
        m.c[(1,)] = m.x >= 1
        m.c[(2,)] = m.x >= 2
        m.c[2] = m.x >= 3
        self.assertIs(ComponentUID(m.c[0]).find_component_on(m), m.c[0])
        self.assertIs(ComponentUID('c[0]').find_component_on(m), m.c[0])
        self.assertIsNone(ComponentUID('c[(0,)]').find_component_on(m))
        self.assertIs(ComponentUID(m.c[(1,)]).find_component_on(m), m.c[(1,)])
        self.assertIs(ComponentUID('c[(1,)]').find_component_on(m), m.c[(1,)])
        self.assertIsNone(ComponentUID('c[1]').find_component_on(m))
        self.assertIs(ComponentUID('c[(2,)]').find_component_on(m), m.c[(2,)])
        self.assertIs(ComponentUID('c[2]').find_component_on(m), m.c[2])
        self.assertEqual(len(m.c), 4)

        self.assertEqual(repr(ComponentUID(m.c[0])), "c[0]")
        self.assertEqual(repr(ComponentUID(m.c[(1,)])), "c[(1,)]")
        self.assertEqual(str(ComponentUID(m.c[0])), "c[0]")
        self.assertEqual(str(ComponentUID(m.c[(1,)])), "c[(1,)]")

        m = ConcreteModel()
        m.x = Var()
        m.c = Constraint([0, 1])
        m.c[0] = m.x >= 0
        m.c[(1,)] = m.x >= 1
        self.assertIs(ComponentUID(m.c[0]).find_component_on(m), m.c[0])
        self.assertIs(ComponentUID(m.c[(0,)]).find_component_on(m), m.c[0])
        self.assertIs(ComponentUID('c[0]').find_component_on(m), m.c[0])
        self.assertIs(ComponentUID('c[(0,)]').find_component_on(m), m.c[0])
        self.assertIs(ComponentUID(m.c[1]).find_component_on(m), m.c[1])
        self.assertIs(ComponentUID(m.c[(1,)]).find_component_on(m), m.c[1])
        self.assertIs(ComponentUID('c[(1,)]').find_component_on(m), m.c[1])
        self.assertIs(ComponentUID('c[1]').find_component_on(m), m.c[1])
        self.assertEqual(len(m.c), 2)

        m = ConcreteModel()
        m.b = Block(Any)
        m.b[0].c = Block(Any)
        m.b[0].c[0].x = Var()
        m.b[(1,)].c = Block(Any)
        m.b[(1,)].c[(1,)].x = Var()
        ref = m.b[0].c[0].x
        self.assertIs(ComponentUID(ref).find_component_on(m), ref)
        ref = 'm.b[0].c[(0,)].x'
        self.assertIsNone(ComponentUID(ref).find_component_on(m))
        ref = m.b[(1,)].c[(1,)].x
        self.assertIs(ComponentUID(ref).find_component_on(m), ref)
        ref = 'm.b[(1,)].c[1].x'
        self.assertIsNone(ComponentUID(ref).find_component_on(m))

        buf = {}
        ref = m.b[0].c[0].x
        self.assertIs(ComponentUID(ref, cuid_buffer=buf).find_component_on(m), ref)
        self.assertEqual(len(buf), 3)
        ref = 'm.b[0].c[(0,)].x'
        self.assertIsNone(ComponentUID(ref, cuid_buffer=buf).find_component_on(m))
        self.assertEqual(len(buf), 3)
        ref = m.b[(1,)].c[(1,)].x
        self.assertIs(ComponentUID(ref, cuid_buffer=buf).find_component_on(m), ref)
        self.assertEqual(len(buf), 4)
        ref = 'm.b[(1,)].c[1].x'
        self.assertIsNone(ComponentUID(ref, cuid_buffer=buf).find_component_on(m))
        self.assertEqual(len(buf), 4)

    def test_pickle_index(self):
        m = ConcreteModel()
        m.b = Block(Any)

        idx = "|b'foo'"
        m.b[idx].x = Var()
        cuid = ComponentUID(m.b[idx].x)
        self.assertEqual(str(cuid), 'b["|b\'foo\'"].x')
        self.assertIs(cuid.find_component_on(m), m.b[idx].x)
        tmp = ComponentUID(str(cuid))
        self.assertIsNot(cuid, tmp)
        self.assertEqual(cuid, tmp)
        self.assertIs(tmp.find_component_on(m), m.b[idx].x)
        tmp = pickle.loads(pickle.dumps(cuid))
        self.assertIsNot(cuid, tmp)
        self.assertEqual(cuid, tmp)
        self.assertIs(tmp.find_component_on(m), m.b[idx].x)

        idx = _Foo(1, 'a')
        m.b[idx].x = Var()
        cuid = ComponentUID(m.b[idx].x)
        # Note that the pickle string for namedtuple changes between
        # Python 2, 3, and pypy, so we will just check the non-pickle
        # data part
        self.assertRegex(str(cuid), r"^b\[\|b?(['\"]).*\.\1\]\.x$")

        self.assertIs(cuid.find_component_on(m), m.b[idx].x)
        tmp = ComponentUID(str(cuid))
        self.assertIsNot(cuid, tmp)
        self.assertEqual(cuid, tmp)
        self.assertIs(tmp.find_component_on(m), m.b[idx].x)
        tmp = pickle.loads(pickle.dumps(cuid))
        self.assertIsNot(cuid, tmp)
        self.assertEqual(cuid, tmp)
        self.assertIs(tmp.find_component_on(m), m.b[idx].x)

        idx = datetime(1, 2, 3)
        m.b[idx].x = Var()
        cuid = ComponentUID(m.b[idx].x)
        # Note that the pickle string for namedtuple changes between
        # Python 2, 3, and pypy, so we will just check the non-pickle
        # data part
        self.assertRegex(str(cuid), r"^b\[\|b?(['\"]).*\.\1\]\.x$")
        self.assertIs(cuid.find_component_on(m), m.b[idx].x)
        tmp = ComponentUID(str(cuid))
        self.assertIsNot(cuid, tmp)
        self.assertEqual(cuid, tmp)
        self.assertIs(tmp.find_component_on(m), m.b[idx].x)
        tmp = pickle.loads(pickle.dumps(cuid))
        self.assertIsNot(cuid, tmp)
        self.assertEqual(cuid, tmp)
        self.assertIs(tmp.find_component_on(m), m.b[idx].x)

        self.assertEqual(len(m.b), 3)

    def test_deprecated_ComponentUID_location(self):
        import pyomo.core.base.component as comp

        self.assertNotIn('ComponentUID', dir(comp))

        warning = (
            "DEPRECATED: the 'ComponentUID' class has been moved to "
            "'pyomo.core.base.componentuid.ComponentUID'"
        )
        OUT = StringIO()
        with LoggingIntercept(OUT, 'pyomo.core'):
            from pyomo.core.base.component import ComponentUID as old_ComponentUID
        self.assertIn(warning, OUT.getvalue().replace('\n', ' '))

        self.assertIs(old_ComponentUID, ComponentUID)
        self.assertIs(old_ComponentUID, ComponentUID)

        OUT = StringIO()
        with LoggingIntercept(OUT, 'pyomo.core'):
            self.assertIs(comp.ComponentUID, ComponentUID)
        self.assertEqual("", OUT.getvalue())

    def _slice_model(self):
        m = ConcreteModel()

        m.d1_1 = Set(initialize=[1, 2, 3])
        m.d1_2 = Set(initialize=['a', 'b', 'c'])
        m.d1_3 = Set(initialize=[1.1, 1.2, 1.3])
        m.d2 = Set(initialize=[('a', 1), ('b', 2)])
        m.dn = Set(initialize=[('c', 3), ('d', 4, 5)], dimen=None)

        @m.Block()
        def b(b):
            b.b = Block()

            @b.Block(m.d1_1)
            def b1(b1, i):
                b1.v = Var()
                b1.v1 = Var(m.d1_3)
                b1.v2 = Var(m.d1_1, m.d1_2)
                b1.vn = Var(m.dn, m.d1_2)

            @b.Block(m.d1_1, m.d1_2)
            def b2(b2, i, j):
                b2.v = Var()
                b2.v1 = Var(m.d1_3)
                b2.v2 = Var(m.d1_1, m.d1_2)
                b2.vn = Var(m.d1_1, m.dn, m.d1_2)

            @b.Block(m.d1_3, m.d2)
            def b3(b3, i, j, k):
                b3.v = Var()
                b3.v1 = Var(m.d1_3)
                b3.v2 = Var(m.d1_1, m.d1_2)
                b3.vn = Var(m.d1_1, m.dn, m.d2)

            # Don't think I can define a dim-None Block with
            # a rule unless normalize_index.flatten is False.
            b.bn = Block(m.d1_2, m.dn, m.d2)
            # NOTE: These blocks are only defined for 'a', ('a',1)
            # in the first and last "subsets"
            b.bn['a', 'c', 3, 'a', 1].v = Var()
            b.bn['a', 'c', 3, 'a', 1].v1 = Var(m.d1_3)
            b.bn['a', 'c', 3, 'a', 1].v2 = Var(m.d1_1, m.d1_2)
            b.bn['a', 'c', 3, 'a', 1].vn = Var(m.d1_1, m.dn, m.d2)
            b.bn['a', 'd', 4, 5, 'a', 1].v = Var()
            b.bn['a', 'd', 4, 5, 'a', 1].v1 = Var(m.d1_3)
            b.bn['a', 'd', 4, 5, 'a', 1].v2 = Var(m.d1_1, m.d1_2)
            b.bn['a', 'd', 4, 5, 'a', 1].vn = Var(m.d1_1, m.dn, m.d2)

        return m

    def assertListSameComponents(self, m, cuid1, cuid2):
        self.assertTrue(cuid1.list_components(m))
        self.assertEqual(
            len(list(cuid1.list_components(m))), len(list(cuid2.list_components(m)))
        )
        for c1, c2 in zip(cuid1.list_components(m), cuid2.list_components(m)):
            self.assertIs(c1, c2)

    def test_cuid_from_slice_1(self):
        """
        These are slices over a single level of the hierarchy.
        """
        m = self._slice_model()

        _slice = m.b[:]
        cuid_str = ComponentUID('b[*]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)

        _slice = m.b.b1[:]
        cuid_str = ComponentUID('b.b1[*]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)

        _slice = m.b.b1[...]
        cuid_str = ComponentUID('b.b1[**]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)

        _slice = m.b.b2[:, 'a']
        cuid_str = ComponentUID('b.b2[*,a]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[...]
        cuid_str = ComponentUID('b.b2[**]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)

        _slice = m.b.b3[1.1, :, 2]
        cuid_str = ComponentUID('b.b3[1.1,*,2]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[:, :, 'b']
        cuid_str = ComponentUID('b.b3[*,*,b]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[1.1, ...]
        cuid_str = ComponentUID('b.b3[1.1,**]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[...]
        cuid_str = ComponentUID('b.b3[**]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))

        _slice = m.b.bn['a', :, :, 'a', 1]
        cuid_str = ComponentUID('b.bn[a,*,*,a,1]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.bn['a', 'c', 3, :, :]
        cuid_str = ComponentUID('b.bn[a,c,3,*,*]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.bn[...]
        cuid_str = ComponentUID('b.bn[**]')
        cuid = ComponentUID(_slice)
        self.assertEqual(cuid, cuid_str)

    def test_cuid_from_slice_2(self):
        """
        These are slices that describe a component
        at a "deeper level" than the original slice.
        """
        m = self._slice_model()

        _slice = m.b[:].b
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b[*].b')
        self.assertEqual(cuid, cuid_str)

        _slice = m.b[:].b1[:].v
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b[*].b1[*].v')
        self.assertEqual(cuid, cuid_str)

        _slice = m.b.b2[2, :].v
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].v')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[2, :].v1[:]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].v1[*]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[2, :].v1[1.1]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].v1[1.1]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertEqual(cuid, cuid_str)
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[2, :].vn[1, ..., :, 'b']
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].vn[1,**,*,b]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[2, :].vn[..., 'b']
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].vn[**,b]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[2, :].vn[..., ...]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].vn[**,**]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[2, :].vn[...]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[2,*].vn[**]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[...].v2[:, 'a']
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[**].v2[*,a]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[:, 'a', :].v1
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b3[*,a,*].v1')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[:, 'a', :].v2[1, 'a']
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b3[*,a,*].v2[1,a]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[:, 'a', :].v2[1, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b3[*,a,*].v2[1,*]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b3[:, 'a', :].vn[1, :, :, 'a', 1]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b3[*,a,*].vn[1,*,*,a,1]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.bn['a', 'c', 3, :, :].vn[1, :, 3, 'a', :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.bn[a,c,3,*,*].vn[1,*,3,a,*]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.bn[...].vn[1, :, 3, 'a', :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.bn[**].vn[1,*,3,a,*]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.bn[...].vn
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.bn[**].vn')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.bn[...].vn[...]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.bn[**].vn[**]')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

    def test_cuid_from_slice_3(self):
        """
        "3-level" slices. These test the ability of
        the slice-processing logic to handle multiple
        `get_item` calls in a hierarchy.
        """
        m = self._slice_model()

        _slice = m.b[:].b3[:, 'a', :].v2[1, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b[*].b3[*,a,*].v2[1,*]')
        self.assertEqual(cuid, cuid_str)
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b[:].b3[:, 'a', :].v2
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b[*].b3[*,a,*].v2')
        self.assertEqual(cuid, cuid_str)
        self.assertListSameComponents(m, cuid, cuid_str)

    def test_cuid_from_slice_with_call(self):
        m = self._slice_model()

        _slice = m.b.component('b2')[:, 'a'].v2[1, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[*,a].v2[1,*]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        # This works as find_component is not in the
        # _call_stack of the slice.
        _slice = m.b.find_component('b2')[:, 'a'].v2[1, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[*,a].v2[1,*]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b[:].component('b2')
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b[*].b2')
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b[:].component('b2', 'b1')
        with self.assertRaisesRegex(ValueError, '.*multiple arguments.*'):
            cuid = ComponentUID(_slice)

        # call of something other than component
        _slice = IndexedComponent_slice(
            m.b[:].fix, (IndexedComponent_slice.call, ('fix',), {})
        )
        with self.assertRaisesRegex(
            ValueError,
            "Cannot create a CUID from a slice with a call to any "
            r"method other than 'component': got 'fix'\.",
        ):
            cuid = ComponentUID(_slice)

        _slice = IndexedComponent_slice(
            m.b[:].component('v'), (IndexedComponent_slice.call, ('fix',), {})
        )
        with self.assertRaisesRegex(
            ValueError,
            "Cannot create a CUID with a __call__ of anything "
            "other than a 'component' attribute",
        ):
            cuid = ComponentUID(_slice)

        _slice = m.b[:].component('b2', kwd=None)
        with self.assertRaisesRegex(ValueError, '.*call that contains keywords.*'):
            cuid = ComponentUID(_slice)

        _slice = m.b.b2[:, 'a'].component('vn')[:, 'c', 3, :, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[*,a].vn[*,c,3,*,*]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[1, 'a'].component('vn')[:, 'c', 3, :, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[1,a].vn[*,c,3,*,*]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[...].component('vn')[:, 'c', 3, :, :]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[**].vn[*,c,3,*,*]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

        _slice = m.b.b2[:, 'a'].component('vn')[...]
        cuid = ComponentUID(_slice)
        cuid_str = ComponentUID('b.b2[*,a].vn[**]')
        self.assertEqual(cuid, cuid_str)
        self.assertEqual(str(cuid), str(cuid_str))
        self.assertListSameComponents(m, cuid, cuid_str)

    def test_cuid_from_slice_errors(self):
        # two getitem
        m = self._slice_model()
        m.b.comp = Reference(m.b.b1[:].v1)
        _slice = m.b[:].comp[1][1.1]
        with self.assertRaisesRegex(ValueError, r'.*Two `get_item` calls.*'):
            cuid = ComponentUID(_slice)

        _slice = IndexedComponent_slice(
            m.b[:].component('v'), (IndexedComponent_slice.del_attribute, ('foo',))
        )
        with self.assertRaisesRegex(
            ValueError,
            "Cannot create a CUID from a slice that "
            "contains `set` or `del` calls: got call %s "
            r"with argument \('foo',\)" % (IndexedComponent_slice.del_attribute,),
        ):
            cuid = ComponentUID(_slice)

    def test_cuid_from_cuid(self):
        def assert_equal(cuid1, cuid2):
            self.assertEqual(cuid1, cuid2)
            self.assertFalse(cuid1 is cuid2)

        cuid_str = ComponentUID("b.var[1]")
        cuid_str_2 = ComponentUID(cuid_str)
        assert_equal(cuid_str, cuid_str_2)

        cuid_comp = ComponentUID(self.m.b[1, 1].c)
        cuid_comp_2 = ComponentUID(cuid_comp)
        assert_equal(cuid_str, cuid_str_2)

        cuid_slice = ComponentUID(self.m.b[1, :].c)
        cuid_slice_2 = ComponentUID(cuid_slice)
        assert_equal(cuid_slice, cuid_slice_2)

        with self.assertRaisesRegex(ValueError, "Context is not allowed"):
            ComponentUID(cuid_comp, context=self.m.b[1, 1])


if __name__ == "__main__":
    unittest.main()
