def test_list(self): data = ImmutableList((1, 2, 3)) self.assertEqual(data.meta, {}) data.meta["been here"] = True self.assertTrue(ImmutableList((1, 2, 3)).meta["been here"])
def test_iter(self): il = ImmutableList([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) self.assertEqual([x for x in il], list(range(10))) self.assertEqual([x for x in iter(il)], list(range(10))) self.assertEqual([x for x in il.__iter__()], list(range(10))) self.assertFalse([x for x in ImmutableList()])
def example_immutable_data(): return ImmutableDict( foo=1, bar=ImmutableDict(baz=99), xxx=ImmutableList((None, True, ImmutableList())), yyy=ImmutableDict(), zzz=None, )
def test_pass_immutable(self): self.assertTrue( sanitize([[]]) is sanitize(ImmutableList([[]])) is sanitize( ImmutableList([ImmutableList()])) is ImmutableList( [ImmutableList()])) self.assertTrue( sanitize({"a": {}}) is sanitize(ImmutableDict({"a": {}})) is sanitize(ImmutableDict( a={})) is sanitize(ImmutableDict( a=ImmutableDict())) is ImmutableDict(a=ImmutableDict()))
def test_index(self): with self.assertRaises(ValueError): ImmutableList().index(None) with self.assertRaises(ValueError): ImmutableList().index(1) with self.assertRaises(ValueError): ImmutableList([1, 2, 3]).index(4) self.assertEqual(ImmutableList([1, 2, 3, 4]).index(3), 2) with self.assertRaises(ValueError): ImmutableList([1, 2, 3, 4]).index(3.0)
def test_simple(self): self.assertTrue(sanitize(ImmutableDict()) is ImmutableDict()) self.assertTrue(sanitize({}) is ImmutableDict()) self.assertTrue(sanitize(ImmutableList()) is ImmutableList()) self.assertTrue(sanitize([]) is ImmutableList()) self.assertTrue(sanitize(None) is None) self.assertTrue(sanitize(True) is True) self.assertTrue(sanitize(False) is False) self.assertEqual(sanitize("foobar"), "foobar") self.assertEqual(sanitize(1234), 1234) self.assertEqual(sanitize(1.234), 1.234)
def test_list(self): ch = ImmutableChecker(self) ch.addImmutable(ImmutableList()) ch.addImmutable(ImmutableList([1])) ch.addImmutable(ImmutableList([1, 2])) ch.addImmutable(ImmutableList([1, "foo"])) ch.addImmutable(ImmutableList([1, None])) ch.addImmutable(ImmutableList([1, True])) ch.addImmutable(ImmutableList([1, 9.876])) ch.addNonImmutable(ImmutableList([1, 2, Exception()])) ch.addImmutable(ImmutableList([1, 2, Exception()])[0:2]) ch.addImmutable(ImmutableList([99, 2, Exception()])[0:2]) ch.check_all()
def test_hash(self): data1 = make_test_data() data2 = make_test_data() if all(x is y for x, y in zip(data1, data2)): # If the members of data1 and data2 are pairwise identical (they # are the same objects), then this test is not meaningful. # This means that constructing the same tuple or string twice # yields the same object. This was not the case with the Python # interpreter used during development. raise unittest.SkipTest("Test data sets contain identical objects") l1 = ImmutableList(data1) l2 = ImmutableList(data2) self.assertTrue(l1 is l2)
def subscribe( self, plugin_info, paths, callback, *, initial=True, state_type=StateType.RESOLVED, ): if plugin_info.disabled: raise Exception("disabled plugin must not subscribe") slots = tuple( SubscriptionSlot( path, ( self.__rawSubscriptionRoot if state_type == StateType.RAW else self.__unresolvedSubscriptionRoot if state_type == StateType.UNRESOLVED else self.__resolvedSubscriptionRoot ).get(path), state_type, ) for path in map(makePath, paths) ) state = ImmutableList(slot.directory.state for slot in slots) sub = Subscription(plugin_info, slots, callback, state, initial) plugin_info.subscriptions.add(sub) for idx, slot in enumerate(slots): slot.directory.subscriptions.add((sub, idx)) if initial: self.__updatedSubscriptions.add(sub) self.__stateUpdateEvent.set() return lambda: self.unsubscribe(sub)
def setDataForPath(data, path: PathType, value, *, undefined=ImmutableDict()): if not path: if value is Undefined: return undefined else: return sanitize(value) p, path = path[0], path[1:] if type(p) is str: if data is Undefined: data = ImmutableDict() elif type(data) is not ImmutableDict: raise PathElementTypeMismatch(p, data) if value is Undefined and not path: return data.discard(p) elif type(p) is int: if data is Undefined: data = ImmutableList() elif type(data) is not ImmutableList: raise PathElementTypeMismatch(p, data) if value is Undefined and not path: if 0 <= p < len(data): return data[0:p] + data[p + 1:] else: return data if p < 0 or p > len(data): raise IndexError if p == len(data): return data.append(setDataForPath(Undefined, path, value)) else: raise InvalidPathElement(p) return data.set(p, setDataForPath(data.get(p, Undefined), path, value))
def plugins(data): if type(data) is ImmutableDict: if "__plugin__" in data: return ImmutableList([((), data)]) gen = ( sorted(data.items()) if type(data) is ImmutableDict else enumerate(data) ) return ImmutableList( ((key,) + path, plugin) for key, value in gen if type(value) in (ImmutableDict, ImmutableList) for path, plugin in plugins(value) )
def sanitize(data): t = type(data) if (t is ImmutableList or t is ImmutableDict) and data.isImmutableJson: return data if data is None or t is bool or t is str or t is int or t is float: return data elif isinstance(data, Sequence): data = ImmutableList(sanitize(x) for x in data if x is not Undefined) if data.isImmutableJson: return data elif isinstance(data, Mapping): data = ImmutableDict((enforceKeyType(key), sanitize(value)) for key, value in data.items() if value is not Undefined) if data.isImmutableJson: return data raise TypeError("data cannot be sanitized to immutable json")
def test_concat_extend(self): lists = [[], [0], ["foo", "bar"], [False, True, None, 0.123]] for l1 in lists: for l2 in lists: il1 = ImmutableList(l1) il2 = ImmutableList(l2) self.assertTrue(ImmutableList(l1 + l2) is il1 + il2) self.assertTrue(ImmutableList(l1 + l2) is il1.extend(il2)) self.assertTrue(ImmutableList(l1 + l2) is il1.extend(l2))
def test_list_weakref(self): # Create one ImmutableList self.assertEqual(ImmutableList._get_instance_count(), 0) d = ImmutableList([1, 2]) self.assertEqual(ImmutableList._get_instance_count(), 1) # Store in a weakref.WeakValueListionary wvd = weakref.WeakValueDictionary() wvd[1] = d self.assertEqual(ImmutableList._get_instance_count(), 1) self.assertEqual(len(wvd), 1) self.assertTrue(wvd[1] is d) # Release the only reference to the ImmutableList d = None self.assertEqual(ImmutableList._get_instance_count(), 0) # Check that it is no longer in wkd self.assertEqual(len(wvd), 0) with self.assertRaises(KeyError): wvd[1] # Create a new ImmutableList, check that it not magically appears in # wkd d = ImmutableList([1, 2]) self.assertEqual(ImmutableList._get_instance_count(), 1) self.assertEqual(len(wvd), 0) with self.assertRaises(KeyError): wvd[1]
def symlinks(data): sl = symlink(data) if sl is False: return ImmutableList() elif sl is not None: return ImmutableList([((), sl)]) gen = ( sorted(data.items()) if type(data) is ImmutableDict else enumerate(data) ) return ImmutableList( ((key,) + path, dest) for key, value in gen if type(value) in (ImmutableDict, ImmutableList) for path, dest in symlinks(value) )
def _realpaths(data): rp = functools.partial(_realpathImpl, dict(data)) return ImmutableList( (location, real_destination) for location, real_destination in ( (location, rp(destination)) for location, destination in data ) if real_destination is not None )
def test_append_get_set(self): l0 = ImmutableList() self.assertEqual(len(l0), 0) self.assertEqual(list(l0), []) l1 = l0.append(999) self.assertEqual(len(l1), 1) self.assertEqual(l1[0], 999) self.assertEqual(list(l1), [999]) l2 = l1.append("foobar") self.assertEqual(len(l2), 2) self.assertEqual(l2[0], 999) self.assertEqual(l2[1], "foobar") self.assertEqual(list(l2), [999, "foobar"]) l3 = l2.set(0, "baz") self.assertEqual(len(l3), 2) self.assertEqual(l3[0], "baz") self.assertEqual(l3[1], "foobar") self.assertEqual(list(l3), ["baz", "foobar"])
def test_nested(self): self.assertTrue( sanitize({ "a": 1, "b": None, "c": { "x": True }, "d": ImmutableDict(y=False), }) is ImmutableDict( a=1, b=None, c=ImmutableDict(x=True), d=ImmutableDict( y=False))) self.assertTrue( sanitize([1, None, [True], ImmutableList([False])]) is ImmutableList( [1, None, ImmutableList([True]), ImmutableList([False])]))
def test_count(self): il = ImmutableList([3, "two", 1, 3, "two", 3, 2]) self.assertEqual(il.count(0), 0) self.assertEqual(il.count(1), 1) self.assertEqual(il.count("two"), 2) self.assertEqual(il.count(3), 3) self.assertEqual(il.count(3.0), 0)
def _resolveImpl(resolve_data): if len(resolve_data) == 3 and resolve_data[1] == (): return resolve_data[2] data = resolve_data[0] replacements = [ (resolve_data[i], resolve_data[i + 1]) for i in range(1, len(resolve_data), 2) ] if not replacements: return data try: keep_alive = data.meta["_resolveImpl-keep_alive"] except KeyError: keep_alive = data.meta["_resolveImpl-keep_alive"] = [] keep_alive = [] i = 0 while i < len(replacements): first_elem = replacements[i][0][0] j = i + 1 while j < len(replacements): if replacements[j][0][0] == first_elem: j += 1 else: break if j - i == 1 and len(replacements[i][0]) == 1: data = _set_helper(data, first_elem, replacements[i][1]) else: subreplacements = ImmutableList( [ _get_helper(data, first_elem), *( item for k in range(i, j) for item in ( replacements[k][0][1:], replacements[k][1], ) ), ] ) keep_alive.append(subreplacements) data = _set_helper(data, first_elem, _resolveImpl(subreplacements)) i = j return data
def __init__(self): self.__rawState = self.__unresolvedState = ImmutableDict() self.__resolvedState = self.__nextState = ImmutableDict() self.__realpath = makePath self.__rawSubscriptionRoot = Directory(None, None) self.__unresolvedSubscriptionRoot = Directory(None, None) self.__resolvedSubscriptionRoot = Directory(None, None) self.__updatedSubscriptions = set() self.__pluginInfos = [] self.__pluginList = ImmutableList() self.__coreState = ImmutableDict(commands=ImmutableDict()) self.__corePluginPaths = [] self.__commands = {} # path -> {name: Command} self.__stateUpdateEvent = asyncio.Event() self.__stateUpdateTask = asyncio.create_task( self.__stateUpdateTaskImpl() ) self.__stateUpdateTask.add_done_callback(print_exception_task_callback)
def __init__(self, plugin, slots, callback, state, initial: bool): if ( type(slots) != tuple or any(type(slot) is not SubscriptionSlot for slot in slots) or type(state) != ImmutableList or not len(slots) == len(state) ): logging.error( # FIXME REMOVE "Subscription constructor called with invalid arguments: " f"slots={ slots !r} len(state)={ len(state) }" ) raise Exception( "Subscription constructor called with invalid arguments: " f"slots={ slots !r} len(state)={ len(state) }" ) self.plugin = plugin self.slots = slots self.callback = callback self.__currentState = state self.__reportedState = ( ImmutableList(Undefined for _ in slots) if initial else state ) self.disabled = False
def test_strict_equality(self): """ pyimmutable has stricter equality requirements than usual. """ l1 = ImmutableList([1, 2, 3]) l2 = ImmutableList([1, 2, 3.0]) self.assertFalse(l1 is l2) self.assertNotEqual(l1, l2) self.assertEqual(l1.count(3), 1) self.assertEqual(l1.count(3.0), 0) self.assertEqual(l2.count(3), 0) self.assertEqual(l2.count(3.0), 1) self.assertEqual(l1.index(3), 2) self.assertEqual(l2.index(3.0), 2) with self.assertRaises(ValueError): l1.index(3.0) with self.assertRaises(ValueError): l2.index(3)
def test_reversed(self): il = ImmutableList([9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) self.assertEqual([x for x in reversed(il)], list(range(10))) self.assertEqual([x for x in il.__reversed__()], list(range(10))) self.assertEqual([x for x in iter(il.__reversed__())], list(range(10)))
def immutables_count(): return (ImmutableDict._get_instance_count() + ImmutableList._get_instance_count())
def test_mixed(self): ch = ImmutableChecker(self) keys = ( "", "a", "foobar", "baz", "0", "123", "xxx", None, False, unittest, ) values = keys + ( 123, 0, -1, 12.34, 567.76, True, [], {}, [1, 2], { "a": 1 }, ) idicts = set() for k1 in keys: for v1 in values: i = ImmutableDict().set(k1, v1) idicts.add(i) ch.add(i) for k2 in keys: for v2 in values: i = i.set(k2, v2) idicts.add(i) ch.add(i) for i in set(idicts): for k in keys: i = i.discard(k) idicts.add(i) ch.add(i) ilists = set() for k1 in keys: i = ImmutableList().append(k1) ilists.add(i) ch.add(i) for k1 in keys: i = i.append(k2) ilists.add(i) ch.add(i) idicts = list(idicts) ilists = list(ilists) for n in range(100_000): d1 = idicts[n * 104_651 % len(idicts)] l1 = ilists[n * 104_659 % len(ilists)] k1 = keys[n * 104_677 % len(keys)] k2 = keys[n * 104_681 % len(keys)] d2 = idicts[n * 104_683 % len(idicts)] l2 = ilists[n * 104_693 % len(ilists)] nl = l1.append(d1) ilists.append(nl) ch.add(nl) nd = d1.set(k1, l1) idicts.append(nd) ch.add(nd) if k2 in d1: nd2 = d1.discard(k2) idicts.append(nd2) ch.add(nd2) nl = l2.append(l1) ilists.append(nl) ch.add(nl) nd = d2.set(k2, d1) idicts.append(nd) ch.add(nd)