More `._addr` boxing refinements
The more I think about it, it seems @guille's orig approach of
unwrapping UDS socket-file addresses to strings (or `Path`) is making
the most sense. I had originally thought that pairing it with the
listening side's pid would add clarity (and it definitely does for
introspection/debug/logging) but since we don't end up passing that pid
to the eventual `.connect()` call on the client side, it doesn't make
much sense to wrap it for the wire just to discard.. Further, the
`tuple[str, int]` makes `wrap_address()` break for TCP since it will
always match on uds first.
So, on that note this patch refines a few things in prep for going back
to that original `UnwrappedAddress` as `str` type though longer run
i think the more "builtin approach" would be to add `msgspec` codec
hooks for these types to avoid all the `.wrap()`/`.unwrap()` calls
throughout the runtime.
Down-low deats,
- add `wrap_address()` doc string, detailed (todo) comments and handle
  the `[None, None]` case that can come directly from
  `._state._runtime_vars['_root_mailbox']`.
- buncha adjustments to `UDSAddress`,
  - add a `filedir`, chng `filepath` -> `filename` and mk `maybe_pid` optional.
  - the intent `filedir` is act as the equivalent of the host part in a network proto's
    socket address and when it's null use the `.def_bindspace = get_rt_dir()`.
  - always ensure the `filedir / filename` is an absolute path and
    expose it as a new `.sockpath: Path` property.
  - mk `.is_valid` actually verify the `.sockpath` is in the valid
    `.bindspace: namely just checking it's in the expected dir.
  - add pedantic `match:`ing to `.from_addr()` such that we error on
    unexpected `type(addr)` inputs and otherwise parse any `sockpath:
    Path` inputs using a new `unwrap_sockpath()` which simply splits an
    abs file path to dir, file-name parts.
  - `.unwrap()` now just `str`-ifies the `.sockpath: Path`
  - adjust `.open/close_listener()` to use `.sockpath`.
			
			
			
		
							parent
							
								
									3091316b0a
								
							
						
					
					
						commit
						9f837161ea
					
				
							
								
								
									
										150
									
								
								tractor/_addr.py
								
								
								
								
							
							
						
						
									
										150
									
								
								tractor/_addr.py
								
								
								
								
							|  | @ -217,7 +217,14 @@ class TCPAddress(Address): | |||
|         cls, | ||||
|         addr: tuple[str, int] | ||||
|     ) -> TCPAddress: | ||||
|         return TCPAddress(addr[0], addr[1]) | ||||
|         match addr: | ||||
|             case (str(), int()): | ||||
|                 return TCPAddress(addr[0], addr[1]) | ||||
|             case _: | ||||
|                 raise ValueError( | ||||
|                     f'Invalid unwrapped address for {cls}\n' | ||||
|                     f'{addr}\n' | ||||
|                 ) | ||||
| 
 | ||||
|     def unwrap(self) -> tuple[str, int]: | ||||
|         return ( | ||||
|  | @ -228,7 +235,6 @@ class TCPAddress(Address): | |||
|     @classmethod | ||||
|     def get_random( | ||||
|         cls, | ||||
|         current_actor: Actor, | ||||
|         bindspace: str = def_bindspace, | ||||
|     ) -> TCPAddress: | ||||
|         return TCPAddress(bindspace, 0) | ||||
|  | @ -275,6 +281,15 @@ class TCPAddress(Address): | |||
|         ... | ||||
| 
 | ||||
| 
 | ||||
| def unwrap_sockpath( | ||||
|     sockpath: Path, | ||||
| ) -> tuple[Path, Path]: | ||||
|     return ( | ||||
|         sockpath.parent, | ||||
|         sockpath.name, | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| class UDSAddress(Address): | ||||
|     # TODO, maybe we should use better field and value | ||||
|     # -[x] really this is a `.protocol_key` not a "name" of anything. | ||||
|  | @ -287,23 +302,36 @@ class UDSAddress(Address): | |||
| 
 | ||||
|     def __init__( | ||||
|         self, | ||||
|         filepath: str|Path, | ||||
|         maybe_pid: int, | ||||
|         # ^XXX, in the sense you can also pass | ||||
|         filedir: Path|str|None, | ||||
|         # TODO, i think i want `.filename` here? | ||||
|         filename: str|Path, | ||||
| 
 | ||||
|         # XXX, in the sense you can also pass | ||||
|         # a "non-real-world-process-id" such as is handy to represent | ||||
|         # our host-local default "port-like" key for the very first | ||||
|         # root actor to create a registry address. | ||||
|         maybe_pid: int|None = None, | ||||
|     ): | ||||
|         self._filepath: Path = Path(filepath).absolute() | ||||
|         fdir = self._filedir = Path(filedir or self.def_bindspace).absolute() | ||||
|         fpath = self._filepath = Path(filename) | ||||
|         fp: Path = fdir / fpath | ||||
|         assert fp.is_absolute() | ||||
| 
 | ||||
|         # to track which "side" is the peer process by reading socket | ||||
|         # credentials-info. | ||||
|         self._pid: int = maybe_pid | ||||
| 
 | ||||
|     @property | ||||
|     def sockpath(self) -> Path: | ||||
|         return self._filedir / self._filepath | ||||
| 
 | ||||
|     @property | ||||
|     def is_valid(self) -> bool: | ||||
|         ''' | ||||
|         We block socket files not allocated under the runtime subdir. | ||||
| 
 | ||||
|         ''' | ||||
|         return self.bindspace in self._filepath.parents | ||||
|         return self.bindspace in self.sockpath.parents | ||||
| 
 | ||||
|     @property | ||||
|     def bindspace(self) -> Path: | ||||
|  | @ -312,23 +340,43 @@ class UDSAddress(Address): | |||
|         just the sub-directory in which we allocate socket files. | ||||
| 
 | ||||
|         ''' | ||||
|         return self.def_bindspace | ||||
|         return self._filedir or self.def_bindspace | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_addr( | ||||
|         cls, | ||||
|         addr: tuple[Path, int] | ||||
|         addr: ( | ||||
|             tuple[Path|str|None, int] | ||||
|             |Path|str | ||||
|         ), | ||||
|     ) -> UDSAddress: | ||||
|         return UDSAddress( | ||||
|             filepath=addr[0], | ||||
|             maybe_pid=addr[1], | ||||
|         ) | ||||
|         match addr: | ||||
|             case tuple()|list(): | ||||
|                 sockpath: Path = Path(addr[0]) | ||||
|                 filedir, filename = unwrap_sockpath(sockpath) | ||||
|                 pid: int = addr[1] | ||||
|                 return UDSAddress( | ||||
|                     filedir=filedir, | ||||
|                     filename=filename, | ||||
|                     maybe_pid=pid, | ||||
|                 ) | ||||
|             # NOTE, in case we ever decide to just `.unwrap()` | ||||
|             # to a `Path|str`? | ||||
|             case str()|Path(): | ||||
|                 sockpath: Path = Path(addr) | ||||
|                 return UDSAddress(*unwrap_sockpath(sockpath)) | ||||
|             case _: | ||||
|                 # import pdbp; pdbp.set_trace() | ||||
|                 raise TypeError( | ||||
|                     f'Bad unwrapped-address for {cls} !\n' | ||||
|                     f'{addr!r}\n' | ||||
|                 ) | ||||
| 
 | ||||
|     def unwrap(self) -> tuple[Path, int]: | ||||
|     def unwrap(self) -> tuple[str, int]: | ||||
|         # XXX NOTE, since this gets passed DIRECTLY to | ||||
|         # `.ipc._uds.open_unix_socket_w_passcred()` | ||||
|         return ( | ||||
|             str(self._filepath), | ||||
|             # XXX NOTE, since this gets passed DIRECTLY to | ||||
|             # `open_unix_socket_w_passcred()` above! | ||||
|             str(self.sockpath), | ||||
|             self._pid, | ||||
|         ) | ||||
| 
 | ||||
|  | @ -338,7 +386,7 @@ class UDSAddress(Address): | |||
|         bindspace: Path|None = None,  # default netns | ||||
|     ) -> UDSAddress: | ||||
| 
 | ||||
|         bs: Path = bindspace or get_rt_dir() | ||||
|         filedir: Path = bindspace or cls.def_bindspace | ||||
|         pid: int = os.getpid() | ||||
|         actor: Actor|None = current_actor( | ||||
|             err_on_no_runtime=False, | ||||
|  | @ -351,30 +399,27 @@ class UDSAddress(Address): | |||
|                 prefix: str = 'root' | ||||
|             sockname: str = f'{prefix}@{pid}' | ||||
| 
 | ||||
|         sockpath: Path = Path(f'{bs}/{sockname}.sock') | ||||
|         sockpath: Path = Path(f'{sockname}.sock') | ||||
|         return UDSAddress( | ||||
|             # filename=f'{tempfile.gettempdir()}/{uuid4()}.sock' | ||||
|             filepath=sockpath, | ||||
|             filedir=filedir, | ||||
|             filename=sockpath, | ||||
|             maybe_pid=pid, | ||||
|         ) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_root(cls) -> Address: | ||||
|         def_uds_filepath: Path = ( | ||||
|             get_rt_dir() | ||||
|             / | ||||
|             'registry@1616.sock' | ||||
|         ) | ||||
|         def_uds_filepath: Path = 'registry@1616.sock' | ||||
|         return UDSAddress( | ||||
|             filepath=def_uds_filepath, | ||||
|             maybe_pid=1616 | ||||
|             filedir=None, | ||||
|             filename=def_uds_filepath, | ||||
|             maybe_pid=1616, | ||||
|         ) | ||||
| 
 | ||||
|     def __repr__(self) -> str: | ||||
|         return ( | ||||
|             f'{type(self).__name__}' | ||||
|             f'[' | ||||
|             f'({self._filepath}, {self._pid})' | ||||
|             f'({self.sockpath}, {self._pid})' | ||||
|             f']' | ||||
|         ) | ||||
| 
 | ||||
|  | @ -391,7 +436,7 @@ class UDSAddress(Address): | |||
|         self, | ||||
|         **kwargs, | ||||
|     ) -> SocketListener: | ||||
|         self._sock = socket.socket( | ||||
|         sock = self._sock = socket.socket( | ||||
|             socket.AF_UNIX, | ||||
|             socket.SOCK_STREAM | ||||
|         ) | ||||
|  | @ -400,8 +445,10 @@ class UDSAddress(Address): | |||
|             f'>[\n' | ||||
|             f'|_{self}\n' | ||||
|         ) | ||||
|         await self._sock.bind(self._filepath) | ||||
|         self._sock.listen(1) | ||||
| 
 | ||||
|         bindpath: Path = self.sockpath | ||||
|         await sock.bind(str(bindpath)) | ||||
|         sock.listen(1) | ||||
|         log.info( | ||||
|             f'Listening on UDS socket\n' | ||||
|             f'[>\n' | ||||
|  | @ -411,7 +458,7 @@ class UDSAddress(Address): | |||
| 
 | ||||
|     def close_listener(self): | ||||
|         self._sock.close() | ||||
|         os.unlink(self._filepath) | ||||
|         os.unlink(self.sockpath) | ||||
| 
 | ||||
| 
 | ||||
| preferred_transport: str = 'uds' | ||||
|  | @ -455,26 +502,55 @@ def mk_uuid() -> str: | |||
| def wrap_address( | ||||
|     addr: UnwrappedAddress | ||||
| ) -> Address: | ||||
|     ''' | ||||
|     Wrap an `UnwrappedAddress` as an `Address`-type based | ||||
|     on matching builtin python data-structures which we adhoc | ||||
|     use for each. | ||||
| 
 | ||||
|     XXX NOTE, careful care must be placed to ensure | ||||
|     `UnwrappedAddress` cases are **definitely unique** otherwise the | ||||
|     wrong transport backend may be loaded and will break many | ||||
|     low-level things in our runtime in a not-fun-to-debug way! | ||||
| 
 | ||||
|     XD | ||||
| 
 | ||||
|     ''' | ||||
|     if is_wrapped_addr(addr): | ||||
|         return addr | ||||
| 
 | ||||
|     cls: Type|None = None | ||||
|     # if 'sock' in addr[0]: | ||||
|     #     import pdbp; pdbp.set_trace() | ||||
|     match addr: | ||||
|         case ( | ||||
|         # TODO! BUT THIS WILL MATCH FOR TCP !... | ||||
|         # -[ ] so prolly go back to what guille had orig XD | ||||
|         #   a plain ol' `str`? | ||||
|         case (( | ||||
|             str()|Path(), | ||||
|             int(), | ||||
|         ): | ||||
|         )): | ||||
|             cls = UDSAddress | ||||
| 
 | ||||
|         case tuple() | list(): | ||||
|         # classic network socket-address as tuple/list | ||||
|         case ( | ||||
|             (str(), int()) | ||||
|             | | ||||
|             [str(), int()] | ||||
|         ): | ||||
|             cls = TCPAddress | ||||
| 
 | ||||
|         case None: | ||||
|         # likely an unset UDS or TCP reg address as defaulted in | ||||
|         # `_state._runtime_vars['_root_mailbox']` | ||||
|         case ( | ||||
|             None | ||||
|             | | ||||
|             [None, None] | ||||
|         ): | ||||
|             cls: Type[Address] = get_address_cls(preferred_transport) | ||||
|             addr: UnwrappedAddress = cls.get_root().unwrap() | ||||
| 
 | ||||
|         case _: | ||||
|             # import pdbp; pdbp.set_trace() | ||||
|             raise TypeError( | ||||
|                 f'Can not wrap address {type(addr)}\n' | ||||
|                 f'{addr!r}\n' | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue