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, |         cls, | ||||||
|         addr: tuple[str, int] |         addr: tuple[str, int] | ||||||
|     ) -> TCPAddress: |     ) -> 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]: |     def unwrap(self) -> tuple[str, int]: | ||||||
|         return ( |         return ( | ||||||
|  | @ -228,7 +235,6 @@ class TCPAddress(Address): | ||||||
|     @classmethod |     @classmethod | ||||||
|     def get_random( |     def get_random( | ||||||
|         cls, |         cls, | ||||||
|         current_actor: Actor, |  | ||||||
|         bindspace: str = def_bindspace, |         bindspace: str = def_bindspace, | ||||||
|     ) -> TCPAddress: |     ) -> TCPAddress: | ||||||
|         return TCPAddress(bindspace, 0) |         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): | class UDSAddress(Address): | ||||||
|     # TODO, maybe we should use better field and value |     # TODO, maybe we should use better field and value | ||||||
|     # -[x] really this is a `.protocol_key` not a "name" of anything. |     # -[x] really this is a `.protocol_key` not a "name" of anything. | ||||||
|  | @ -287,23 +302,36 @@ class UDSAddress(Address): | ||||||
| 
 | 
 | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         filepath: str|Path, |         filedir: Path|str|None, | ||||||
|         maybe_pid: int, |         # TODO, i think i want `.filename` here? | ||||||
|         # ^XXX, in the sense you can also pass |         filename: str|Path, | ||||||
|  | 
 | ||||||
|  |         # XXX, in the sense you can also pass | ||||||
|         # a "non-real-world-process-id" such as is handy to represent |         # a "non-real-world-process-id" such as is handy to represent | ||||||
|         # our host-local default "port-like" key for the very first |         # our host-local default "port-like" key for the very first | ||||||
|         # root actor to create a registry address. |         # 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 |         self._pid: int = maybe_pid | ||||||
| 
 | 
 | ||||||
|  |     @property | ||||||
|  |     def sockpath(self) -> Path: | ||||||
|  |         return self._filedir / self._filepath | ||||||
|  | 
 | ||||||
|     @property |     @property | ||||||
|     def is_valid(self) -> bool: |     def is_valid(self) -> bool: | ||||||
|         ''' |         ''' | ||||||
|         We block socket files not allocated under the runtime subdir. |         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 |     @property | ||||||
|     def bindspace(self) -> Path: |     def bindspace(self) -> Path: | ||||||
|  | @ -312,23 +340,43 @@ class UDSAddress(Address): | ||||||
|         just the sub-directory in which we allocate socket files. |         just the sub-directory in which we allocate socket files. | ||||||
| 
 | 
 | ||||||
|         ''' |         ''' | ||||||
|         return self.def_bindspace |         return self._filedir or self.def_bindspace | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def from_addr( |     def from_addr( | ||||||
|         cls, |         cls, | ||||||
|         addr: tuple[Path, int] |         addr: ( | ||||||
|  |             tuple[Path|str|None, int] | ||||||
|  |             |Path|str | ||||||
|  |         ), | ||||||
|     ) -> UDSAddress: |     ) -> UDSAddress: | ||||||
|         return UDSAddress( |         match addr: | ||||||
|             filepath=addr[0], |             case tuple()|list(): | ||||||
|             maybe_pid=addr[1], |                 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 ( |         return ( | ||||||
|             str(self._filepath), |             str(self.sockpath), | ||||||
|             # XXX NOTE, since this gets passed DIRECTLY to |  | ||||||
|             # `open_unix_socket_w_passcred()` above! |  | ||||||
|             self._pid, |             self._pid, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  | @ -338,7 +386,7 @@ class UDSAddress(Address): | ||||||
|         bindspace: Path|None = None,  # default netns |         bindspace: Path|None = None,  # default netns | ||||||
|     ) -> UDSAddress: |     ) -> UDSAddress: | ||||||
| 
 | 
 | ||||||
|         bs: Path = bindspace or get_rt_dir() |         filedir: Path = bindspace or cls.def_bindspace | ||||||
|         pid: int = os.getpid() |         pid: int = os.getpid() | ||||||
|         actor: Actor|None = current_actor( |         actor: Actor|None = current_actor( | ||||||
|             err_on_no_runtime=False, |             err_on_no_runtime=False, | ||||||
|  | @ -351,30 +399,27 @@ class UDSAddress(Address): | ||||||
|                 prefix: str = 'root' |                 prefix: str = 'root' | ||||||
|             sockname: str = f'{prefix}@{pid}' |             sockname: str = f'{prefix}@{pid}' | ||||||
| 
 | 
 | ||||||
|         sockpath: Path = Path(f'{bs}/{sockname}.sock') |         sockpath: Path = Path(f'{sockname}.sock') | ||||||
|         return UDSAddress( |         return UDSAddress( | ||||||
|             # filename=f'{tempfile.gettempdir()}/{uuid4()}.sock' |             filedir=filedir, | ||||||
|             filepath=sockpath, |             filename=sockpath, | ||||||
|             maybe_pid=pid, |             maybe_pid=pid, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def get_root(cls) -> Address: |     def get_root(cls) -> Address: | ||||||
|         def_uds_filepath: Path = ( |         def_uds_filepath: Path = 'registry@1616.sock' | ||||||
|             get_rt_dir() |  | ||||||
|             / |  | ||||||
|             'registry@1616.sock' |  | ||||||
|         ) |  | ||||||
|         return UDSAddress( |         return UDSAddress( | ||||||
|             filepath=def_uds_filepath, |             filedir=None, | ||||||
|             maybe_pid=1616 |             filename=def_uds_filepath, | ||||||
|  |             maybe_pid=1616, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def __repr__(self) -> str: |     def __repr__(self) -> str: | ||||||
|         return ( |         return ( | ||||||
|             f'{type(self).__name__}' |             f'{type(self).__name__}' | ||||||
|             f'[' |             f'[' | ||||||
|             f'({self._filepath}, {self._pid})' |             f'({self.sockpath}, {self._pid})' | ||||||
|             f']' |             f']' | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  | @ -391,7 +436,7 @@ class UDSAddress(Address): | ||||||
|         self, |         self, | ||||||
|         **kwargs, |         **kwargs, | ||||||
|     ) -> SocketListener: |     ) -> SocketListener: | ||||||
|         self._sock = socket.socket( |         sock = self._sock = socket.socket( | ||||||
|             socket.AF_UNIX, |             socket.AF_UNIX, | ||||||
|             socket.SOCK_STREAM |             socket.SOCK_STREAM | ||||||
|         ) |         ) | ||||||
|  | @ -400,8 +445,10 @@ class UDSAddress(Address): | ||||||
|             f'>[\n' |             f'>[\n' | ||||||
|             f'|_{self}\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( |         log.info( | ||||||
|             f'Listening on UDS socket\n' |             f'Listening on UDS socket\n' | ||||||
|             f'[>\n' |             f'[>\n' | ||||||
|  | @ -411,7 +458,7 @@ class UDSAddress(Address): | ||||||
| 
 | 
 | ||||||
|     def close_listener(self): |     def close_listener(self): | ||||||
|         self._sock.close() |         self._sock.close() | ||||||
|         os.unlink(self._filepath) |         os.unlink(self.sockpath) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| preferred_transport: str = 'uds' | preferred_transport: str = 'uds' | ||||||
|  | @ -455,26 +502,55 @@ def mk_uuid() -> str: | ||||||
| def wrap_address( | def wrap_address( | ||||||
|     addr: UnwrappedAddress |     addr: UnwrappedAddress | ||||||
| ) -> Address: | ) -> 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): |     if is_wrapped_addr(addr): | ||||||
|         return addr |         return addr | ||||||
| 
 | 
 | ||||||
|     cls: Type|None = None |     cls: Type|None = None | ||||||
|  |     # if 'sock' in addr[0]: | ||||||
|  |     #     import pdbp; pdbp.set_trace() | ||||||
|     match addr: |     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(), |             str()|Path(), | ||||||
|             int(), |             int(), | ||||||
|         ): |         )): | ||||||
|             cls = UDSAddress |             cls = UDSAddress | ||||||
| 
 | 
 | ||||||
|         case tuple() | list(): |         # classic network socket-address as tuple/list | ||||||
|  |         case ( | ||||||
|  |             (str(), int()) | ||||||
|  |             | | ||||||
|  |             [str(), int()] | ||||||
|  |         ): | ||||||
|             cls = TCPAddress |             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) |             cls: Type[Address] = get_address_cls(preferred_transport) | ||||||
|             addr: UnwrappedAddress = cls.get_root().unwrap() |             addr: UnwrappedAddress = cls.get_root().unwrap() | ||||||
| 
 | 
 | ||||||
|         case _: |         case _: | ||||||
|  |             # import pdbp; pdbp.set_trace() | ||||||
|             raise TypeError( |             raise TypeError( | ||||||
|                 f'Can not wrap address {type(addr)}\n' |                 f'Can not wrap address {type(addr)}\n' | ||||||
|                 f'{addr!r}\n' |                 f'{addr!r}\n' | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue