Allow to use Python binding without Joint Fabric support (#40483)

* Fix rich.Console escape for "["

* Fix __del__ failure in case self._InitLib() raises exception

* Fix some typos

* Allow to use Python binding without Joint Fabric support

* Mark it as function

* Apply LLM suggestion
diff --git a/src/controller/python/matter/ChipDeviceCtrl.py b/src/controller/python/matter/ChipDeviceCtrl.py
index ceb689d..1e94b20 100644
--- a/src/controller/python/matter/ChipDeviceCtrl.py
+++ b/src/controller/python/matter/ChipDeviceCtrl.py
@@ -447,6 +447,7 @@
         # 'chipStack' is dynamically added; referred to in DeviceProxyWrapper class __del__ method
         self._ChipStack = builtins.chipStack  # type: ignore[attr-defined]
         self._dmLib: typing.Any = None
+        self._isActive = False
 
         self._InitLib()
 
@@ -457,7 +458,6 @@
         self.devCtrl = devCtrl
         self.name = name
         self._fabricCheckNodeId = -1
-        self._isActive = False
 
         # 'chipStack' is dynamically added; referred to in DeviceProxyWrapper class __del__ method
         self._Cluster = ChipClusters(builtins.chipStack)  # type: ignore[attr-defined]
@@ -636,7 +636,7 @@
         self._isActive = False
 
     def ShutdownAll(self):
-        ''' 
+        '''
         Shut down all active controllers and reclaim any used resources.
         '''
         #
@@ -839,7 +839,7 @@
         '''
         Establish a PASE session over BLE.
 
-        Warning: This method attempts to establish a new PASE session, even if an open session already exists. 
+        Warning: This method attempts to establish a new PASE session, even if an open session already exists.
         For safer session management that reuses existing sessions, see `FindOrEstablishPASESession`.
 
         Args:
@@ -859,7 +859,7 @@
         '''
         Establish a PASE session over IP.
 
-        Warning: This method attempts to establish a new PASE session, even if an open session already exists. 
+        Warning: This method attempts to establish a new PASE session, even if an open session already exists.
         For safer session management that reuses existing sessions, see `FindOrEstablishPASESession`.
 
         Args:
@@ -880,7 +880,7 @@
         '''
         Establish a PASE session using setUpCode.
 
-        Warning: This method attempts to establish a new PASE session, even if an open session already exists. 
+        Warning: This method attempts to establish a new PASE session, even if an open session already exists.
         For safer session management that reuses existing sessions, see `FindOrEstablishPASESession`.
 
         Args:
@@ -984,7 +984,7 @@
         Check the test commissioner Pase connection sucess.
 
         Args:
-            nodeid (int): Node id of the device.
+            nodeid (int): Node ID of the device.
 
         Returns:
             bool: True if test commissioner Pase connection success, False if not.
@@ -993,10 +993,10 @@
 
     def ResolveNode(self, nodeid):
         '''
-        Resove Node id.
+        Resolve node ID.
 
         Args:
-            nodeid (int): Node id of the device.
+            nodeid (int): Node ID of the device.
         '''
         self.CheckIsActive()
 
@@ -1007,7 +1007,7 @@
         Get the address and port.
 
         Args:
-            nodeid (int): Node id of the device.
+            nodeid (int): Node ID of the device.
 
         Returns:
             tuple: The address and port if no error occurs or None on failure.
@@ -1017,17 +1017,17 @@
         address = create_string_buffer(64)
         port = c_uint16(0)
 
-        # Intentially return None instead of raising exceptions on error
         error = self._ChipStack.Call(
             lambda: self._dmLib.pychip_DeviceController_GetAddressAndPort(
                 self.devCtrl, nodeid, address, 64, pointer(port))
         )
 
+        # Intentionally return None instead of raising exceptions on error
         return (address.value.decode(), port.value) if error == 0 else None
 
     async def DiscoverCommissionableNodes(self, filterType: discovery.FilterType = discovery.FilterType.NONE, filter: typing.Any = None,
                                           stopOnFirst: bool = False, timeoutSecond: int = 5) -> typing.Union[None, CommissionableNode, typing.List[CommissionableNode]]:
-        ''' 
+        '''
         Discover commissionable nodes via DNS-SD with specified filters.
         Supported filters are:
 
@@ -1121,12 +1121,12 @@
         )
 
     class CommissioningWindowPasscode(enum.IntEnum):
-        kOriginalSetupCode = 0,
-        kTokenWithRandomPin = 1,
+        kOriginalSetupCode = 0
+        kTokenWithRandomPin = 1
 
     async def OpenCommissioningWindow(self, nodeid: int, timeout: int, iteration: int,
                                       discriminator: int, option: CommissioningWindowPasscode) -> CommissioningParameters:
-        ''' 
+        '''
         Opens a commissioning window on the device with the given nodeid.
 
         Args:
@@ -1137,7 +1137,7 @@
                 Ignored if option == 0
             discriminator (int): The long discriminator for the DNS-SD advertisement. Valid range: 0-4095
                 Ignored if option == 0
-            option (int): 
+            option (int):
                 0 = kOriginalSetupCode
                 1 = kTokenWithRandomPIN
 
@@ -2437,9 +2437,16 @@
                 c_void_p, c_void_p, c_uint64, c_uint16, c_uint32, c_uint16, c_uint8]
             self._dmLib.pychip_DeviceController_OpenCommissioningWindow.restype = PyChipError
 
-            self._dmLib.pychip_DeviceController_OpenJointCommissioningWindow.argtypes = [
-                c_void_p, c_void_p, c_uint64, c_uint16, c_uint16, c_uint32, c_uint16]
-            self._dmLib.pychip_DeviceController_OpenJointCommissioningWindow.restype = PyChipError
+            try:
+                # NOTE: Joint Fabric is an optional feature in the Matter SDK core library.
+                #       Build with CHIP_DEVICE_CONFIG_ENABLE_JOINT_FABRIC=1 to enable it.
+                self._dmLib.pychip_DeviceController_OpenJointCommissioningWindow.argtypes = [
+                    c_void_p, c_void_p, c_uint64, c_uint16, c_uint16, c_uint32, c_uint16]
+                self._dmLib.pychip_DeviceController_OpenJointCommissioningWindow.restype = PyChipError
+            except AttributeError:
+                def _unsupported_joint_fabric(*args, **kwargs):
+                    raise NotImplementedError("Joint Fabric support is not available in this Matter SDK build.")
+                self._dmLib.pychip_DeviceController_OpenJointCommissioningWindow = _unsupported_joint_fabric
 
             self._dmLib.pychip_TestCommissionerUsed.argtypes = []
             self._dmLib.pychip_TestCommissionerUsed.restype = c_bool
@@ -2532,7 +2539,7 @@
 
 
 class ChipDeviceController(ChipDeviceControllerBase):
-    ''' 
+    '''
     The ChipDeviceCommissioner binding, named as ChipDeviceController
     '''
     # TODO: This class contains DEPRECATED functions, we should update the test scripts to avoid the usage of those functions.
@@ -2803,7 +2810,7 @@
         Instructs the auto-commissioner to perform a matching fabric check before commissioning.
 
         Args:
-            check (bool): Validation fabric before commissioning. 
+            check (bool): Validation fabric before commissioning.
 
         Raises:
             ChipStackError: On failure.
diff --git a/src/controller/python/matter/ChipReplStartup.py b/src/controller/python/matter/ChipReplStartup.py
index 28b6662..d8e38e6 100644
--- a/src/controller/python/matter/ChipReplStartup.py
+++ b/src/controller/python/matter/ChipReplStartup.py
@@ -180,7 +180,7 @@
     console.print(
         '''\t[red]certificateAuthorityManager[blue]:\tManages a list of CertificateAuthority instances.
     \t[red]caList[blue]:\t\t\t\tThe list of CertificateAuthority instances.
-    \t[red]caList\[n].adminList\[m][blue]:\t\tA specific FabricAdmin object at index m for the nth CertificateAuthority instance.''')
+    \t[red]caList\\[n].adminList\\[m][blue]:\t\tA specific FabricAdmin object at index m for the nth CertificateAuthority instance.''')
 
     console.print(
         f'\n\n[blue]Default CHIP Device Controller (NodeId: {devCtrl.nodeId}): '