summary refs log tree commit diff
path: root/pkgs/tools/nix/nixos-render-docs
diff options
context:
space:
mode:
authorpennae <github@quasiparticle.net>2023-02-19 01:33:36 +0100
committerpennae <github@quasiparticle.net>2023-02-21 18:26:40 +0100
commita7c25bb01f2373eb1f113272731e1659bc91de95 (patch)
tree3654ad672f5fa0f4f053e6d2ea4980406ebc7884 /pkgs/tools/nix/nixos-render-docs
parent2ab8e742a541659baa0470e3be8fc7e01ff51175 (diff)
downloadnixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.tar
nixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.tar.gz
nixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.tar.bz2
nixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.tar.lz
nixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.tar.xz
nixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.tar.zst
nixpkgs-a7c25bb01f2373eb1f113272731e1659bc91de95.zip
nixos-render-docs: add Freezable class
for most of our data classes we can use dataclasses.dataclass with
frozen=True or even plain named tuples. the TOC structure we'll need to
generate proper navigation links is most easily represented and used as
a cyclic structure though, and for that we can use neither. if we want
to make the TOC structures immutable (which seems like a good idea)
we'll need a hack of *some* kind, and this hack seems like the least intrusive.
Diffstat (limited to 'pkgs/tools/nix/nixos-render-docs')
-rw-r--r--pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/utils.py21
1 files changed, 21 insertions, 0 deletions
diff --git a/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/utils.py b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/utils.py
new file mode 100644
index 00000000000..3377d1fa4fe
--- /dev/null
+++ b/pkgs/tools/nix/nixos-render-docs/src/nixos_render_docs/utils.py
@@ -0,0 +1,21 @@
+from typing import Any
+
+_frozen_classes: dict[type, type] = {}
+
+# make a derived class freezable (ie, disallow modifications).
+# we do this by changing the class of an instance at runtime when freeze()
+# is called, providing a derived class that is exactly the same except
+# for a __setattr__ that raises an error when called. this beats having
+# a field for frozenness and an unconditional __setattr__ that checks this
+# field because it does not insert anything into the class dict.
+class Freezeable:
+    def freeze(self) -> None:
+        cls = type(self)
+        if not (frozen := _frozen_classes.get(cls)):
+            def __setattr__(instance: Any, n: str, v: Any) -> None:
+                raise TypeError(f'{cls.__name__} is frozen')
+            frozen = type(cls.__name__, (cls,), {
+                '__setattr__': __setattr__,
+            })
+            _frozen_classes[cls] = frozen
+        self.__class__ = frozen