Compare commits
11 Commits
b875b35b98
...
fb04f74605
Author | SHA1 | Date |
---|---|---|
Tyler Goodlet | fb04f74605 | |
Tyler Goodlet | aa1f6fa4b5 | |
Tyler Goodlet | 9002f608ee | |
Tyler Goodlet | 8ebc022535 | |
Tyler Goodlet | e26fa8330f | |
Tyler Goodlet | a2659069c5 | |
Tyler Goodlet | 54699d7a0b | |
Tyler Goodlet | b91ab9e3a8 | |
Tyler Goodlet | cd14c4fe72 | |
Tyler Goodlet | ad40fcd2bc | |
Tyler Goodlet | 508ba510a5 |
|
@ -0,0 +1,18 @@
|
|||
First generate a built disti:
|
||||
|
||||
```
|
||||
python -m pip install --upgrade build
|
||||
python -m build --sdist --outdir dist/alpha5/
|
||||
```
|
||||
|
||||
Then try a test ``pypi`` upload:
|
||||
|
||||
```
|
||||
python -m twine upload --repository testpypi dist/alpha5/*
|
||||
```
|
||||
|
||||
The push to `pypi` for realz.
|
||||
|
||||
```
|
||||
python -m twine upload --repository testpypi dist/alpha5/*
|
||||
```
|
|
@ -1703,15 +1703,28 @@ class Context:
|
|||
# TODO: expose as mod func instead!
|
||||
structfmt = pretty_struct.Struct.pformat
|
||||
if self._in_overrun:
|
||||
log.warning(
|
||||
f'Queueing OVERRUN msg on caller task:\n\n'
|
||||
|
||||
report: str = (
|
||||
f'{flow_body}'
|
||||
|
||||
f'{structfmt(msg)}\n'
|
||||
)
|
||||
over_q: deque = self._overflow_q
|
||||
self._overflow_q.append(msg)
|
||||
|
||||
if len(over_q) == over_q.maxlen:
|
||||
report = (
|
||||
'FAILED to queue OVERRUN msg, OVERAN the OVERRUN QUEUE !!\n\n'
|
||||
+ report
|
||||
)
|
||||
# log.error(report)
|
||||
log.debug(report)
|
||||
|
||||
else:
|
||||
report = (
|
||||
'Queueing OVERRUN msg on caller task:\n\n'
|
||||
+ report
|
||||
)
|
||||
log.debug(report)
|
||||
|
||||
# XXX NOTE XXX
|
||||
# overrun is the ONLY case where returning early is fine!
|
||||
return False
|
||||
|
|
|
@ -609,6 +609,7 @@ class RemoteActorError(Exception):
|
|||
# just after <Type(
|
||||
# |___ ..
|
||||
tb_body_indent=1,
|
||||
boxer_header=self.relay_uid,
|
||||
)
|
||||
|
||||
tail = ''
|
||||
|
|
|
@ -95,6 +95,10 @@ async def open_root_actor(
|
|||
|
||||
hide_tb: bool = True,
|
||||
|
||||
# TODO, a way for actors to augment passing derived
|
||||
# read-only state to sublayers?
|
||||
# extra_rt_vars: dict|None = None,
|
||||
|
||||
) -> Actor:
|
||||
'''
|
||||
Runtime init entry point for ``tractor``.
|
||||
|
|
|
@ -456,10 +456,13 @@ class Actor:
|
|||
)
|
||||
|
||||
if _pre_chan:
|
||||
log.warning(
|
||||
# con_status += (
|
||||
# ^TODO^ swap once we minimize conn duplication
|
||||
f' -> Wait, we already have IPC with `{uid_short}`??\n'
|
||||
# -[ ] last thing might be reg/unreg runtime reqs?
|
||||
# log.warning(
|
||||
log.debug(
|
||||
f'?Wait?\n'
|
||||
f'We already have IPC with peer {uid_short!r}\n'
|
||||
f'|_{_pre_chan}\n'
|
||||
)
|
||||
|
||||
|
|
|
@ -1420,6 +1420,10 @@ def any_connected_locker_child() -> bool:
|
|||
return False
|
||||
|
||||
|
||||
_ctlc_ignore_header: str = (
|
||||
'Ignoring SIGINT while debug REPL in use'
|
||||
)
|
||||
|
||||
def sigint_shield(
|
||||
signum: int,
|
||||
frame: 'frame', # type: ignore # noqa
|
||||
|
@ -1501,7 +1505,9 @@ def sigint_shield(
|
|||
# NOTE: don't emit this with `.pdb()` level in
|
||||
# root without a higher level.
|
||||
log.runtime(
|
||||
f'Ignoring SIGINT while debug REPL in use by child '
|
||||
_ctlc_ignore_header
|
||||
+
|
||||
f' by child '
|
||||
f'{uid_in_debug}\n'
|
||||
)
|
||||
problem = None
|
||||
|
@ -1535,7 +1541,9 @@ def sigint_shield(
|
|||
# NOTE: since we emit this msg on ctl-c, we should
|
||||
# also always re-print the prompt the tail block!
|
||||
log.pdb(
|
||||
'Ignoring SIGINT while pdb REPL in use by root actor..\n'
|
||||
_ctlc_ignore_header
|
||||
+
|
||||
f' by root actor..\n'
|
||||
f'{DebugStatus.repl_task}\n'
|
||||
f' |_{repl}\n'
|
||||
)
|
||||
|
@ -1596,16 +1604,20 @@ def sigint_shield(
|
|||
repl
|
||||
):
|
||||
log.pdb(
|
||||
f'Ignoring SIGINT while local task using debug REPL\n'
|
||||
f'|_{repl_task}\n'
|
||||
_ctlc_ignore_header
|
||||
+
|
||||
f' by local task\n\n'
|
||||
f'{repl_task}\n'
|
||||
f' |_{repl}\n'
|
||||
)
|
||||
elif req_task:
|
||||
log.debug(
|
||||
'Ignoring SIGINT while debug request task is open but either,\n'
|
||||
'- someone else is already REPL-in and has the `Lock`, or\n'
|
||||
'- some other local task already is replin?\n'
|
||||
f'|_{req_task}\n'
|
||||
_ctlc_ignore_header
|
||||
+
|
||||
f' by local request-task and either,\n'
|
||||
f'- someone else is already REPL-in and has the `Lock`, or\n'
|
||||
f'- some other local task already is replin?\n\n'
|
||||
f'{req_task}\n'
|
||||
)
|
||||
|
||||
# TODO can we remove this now?
|
||||
|
|
|
@ -234,7 +234,7 @@ def find_caller_info(
|
|||
_frame2callerinfo_cache: dict[FrameType, CallerInfo] = {}
|
||||
|
||||
|
||||
# TODO: -[x] move all this into new `.devx._code`!
|
||||
# TODO: -[x] move all this into new `.devx._frame_stack`!
|
||||
# -[ ] consider rename to _callstack?
|
||||
# -[ ] prolly create a `@runtime_api` dec?
|
||||
# |_ @api_frame seems better?
|
||||
|
@ -286,3 +286,18 @@ def api_frame(
|
|||
wrapped._call_infos: dict[FrameType, CallerInfo] = _frame2callerinfo_cache
|
||||
wrapped.__api_func__: bool = True
|
||||
return wrapper(wrapped)
|
||||
|
||||
|
||||
# TODO: something like this instead of the adhoc frame-unhiding
|
||||
# blocks all over the runtime!! XD
|
||||
# -[ ] ideally we can expect a certain error (set) and if something
|
||||
# else is raised then all frames below the wrapped one will be
|
||||
# un-hidden via `__tracebackhide__: bool = False`.
|
||||
# |_ might need to dynamically mutate the code objs like
|
||||
# `pdbp.hideframe()` does?
|
||||
# -[ ] use this as a `@acm` decorator as introed in 3.10?
|
||||
# @acm
|
||||
# async def unhide_frame_when_not(
|
||||
# error_set: set[BaseException],
|
||||
# ) -> TracebackType:
|
||||
# ...
|
||||
|
|
|
@ -53,6 +53,7 @@ def pformat_boxed_tb(
|
|||
|
||||
tb_box_indent: int|None = None,
|
||||
tb_body_indent: int = 1,
|
||||
boxer_header: str = '-'
|
||||
|
||||
) -> str:
|
||||
'''
|
||||
|
@ -88,9 +89,9 @@ def pformat_boxed_tb(
|
|||
|
||||
tb_box: str = (
|
||||
f'|\n'
|
||||
f' ------ - ------\n'
|
||||
f' ------ {boxer_header} ------\n'
|
||||
f'{tb_body}'
|
||||
f' ------ - ------\n'
|
||||
f' ------ {boxer_header}- ------\n'
|
||||
f'_|\n'
|
||||
)
|
||||
tb_box_indent: str = (
|
||||
|
|
|
@ -41,8 +41,10 @@ import textwrap
|
|||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Protocol,
|
||||
Type,
|
||||
TYPE_CHECKING,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
from types import ModuleType
|
||||
|
@ -181,7 +183,11 @@ def mk_dec(
|
|||
dec_hook: Callable|None = None,
|
||||
|
||||
) -> MsgDec:
|
||||
'''
|
||||
Create an IPC msg decoder, normally used as the
|
||||
`PayloadMsg.pld: PayloadT` field decoder inside a `PldRx`.
|
||||
|
||||
'''
|
||||
return MsgDec(
|
||||
_dec=msgpack.Decoder(
|
||||
type=spec, # like `MsgType[Any]`
|
||||
|
@ -227,6 +233,13 @@ def pformat_msgspec(
|
|||
join_char: str = '\n',
|
||||
|
||||
) -> str:
|
||||
'''
|
||||
Pretty `str` format the `msgspec.msgpack.Decoder.type` attribute
|
||||
for display in (console) log messages as a nice (maybe multiline)
|
||||
presentation of all supported `Struct`s (subtypes) available for
|
||||
typed decoding.
|
||||
|
||||
'''
|
||||
dec: msgpack.Decoder = getattr(codec, 'dec', codec)
|
||||
return join_char.join(
|
||||
mk_msgspec_table(
|
||||
|
@ -630,31 +643,57 @@ def limit_msg_spec(
|
|||
# # import pdbp; pdbp.set_trace()
|
||||
# assert ext_codec.pld_spec == extended_spec
|
||||
# yield ext_codec
|
||||
#
|
||||
# ^-TODO-^ is it impossible to make something like this orr!?
|
||||
|
||||
# TODO: make an auto-custom hook generator from a set of input custom
|
||||
# types?
|
||||
# -[ ] below is a proto design using a `TypeCodec` idea?
|
||||
#
|
||||
# type var for the expected interchange-lib's
|
||||
# IPC-transport type when not available as a built-in
|
||||
# serialization output.
|
||||
WireT = TypeVar('WireT')
|
||||
|
||||
|
||||
# TODO: make something similar to this inside `._codec` such that
|
||||
# user can just pass a type table of some sort?
|
||||
# -[ ] we would need to decode all msgs to `pretty_struct.Struct`
|
||||
# and then call `.to_dict()` on them?
|
||||
# -[x] we're going to need to re-impl all the stuff changed in the
|
||||
# runtime port such that it can handle dicts or `Msg`s?
|
||||
#
|
||||
# def mk_dict_msg_codec_hooks() -> tuple[Callable, Callable]:
|
||||
# '''
|
||||
# Deliver a `enc_hook()`/`dec_hook()` pair which does
|
||||
# manual convertion from our above native `Msg` set
|
||||
# to `dict` equivalent (wire msgs) in order to keep legacy compat
|
||||
# with the original runtime implementation.
|
||||
#
|
||||
# Note: this is is/was primarly used while moving the core
|
||||
# runtime over to using native `Msg`-struct types wherein we
|
||||
# start with the send side emitting without loading
|
||||
# a typed-decoder and then later flipping the switch over to
|
||||
# load to the native struct types once all runtime usage has
|
||||
# been adjusted appropriately.
|
||||
#
|
||||
# '''
|
||||
# return (
|
||||
# # enc_to_dict,
|
||||
# dec_from_dict,
|
||||
# )
|
||||
# TODO: some kinda (decorator) API for built-in subtypes
|
||||
# that builds this implicitly by inspecting the `mro()`?
|
||||
class TypeCodec(Protocol):
|
||||
'''
|
||||
A per-custom-type wire-transport serialization translator
|
||||
description type.
|
||||
|
||||
'''
|
||||
src_type: Type
|
||||
wire_type: WireT
|
||||
|
||||
def encode(obj: Type) -> WireT:
|
||||
...
|
||||
|
||||
def decode(
|
||||
obj_type: Type[WireT],
|
||||
obj: WireT,
|
||||
) -> Type:
|
||||
...
|
||||
|
||||
|
||||
class MsgpackTypeCodec(TypeCodec):
|
||||
...
|
||||
|
||||
|
||||
def mk_codec_hooks(
|
||||
type_codecs: list[TypeCodec],
|
||||
|
||||
) -> tuple[Callable, Callable]:
|
||||
'''
|
||||
Deliver a `enc_hook()`/`dec_hook()` pair which handle
|
||||
manual convertion from an input `Type` set such that whenever
|
||||
the `TypeCodec.filter()` predicate matches the
|
||||
`TypeCodec.decode()` is called on the input native object by
|
||||
the `dec_hook()` and whenever the
|
||||
`isiinstance(obj, TypeCodec.type)` matches against an
|
||||
`enc_hook(obj=obj)` the return value is taken from a
|
||||
`TypeCodec.encode(obj)` callback.
|
||||
|
||||
'''
|
||||
...
|
||||
|
|
|
@ -30,9 +30,9 @@ from msgspec import (
|
|||
Struct as _Struct,
|
||||
structs,
|
||||
)
|
||||
from pprint import (
|
||||
saferepr,
|
||||
)
|
||||
# from pprint import (
|
||||
# saferepr,
|
||||
# )
|
||||
|
||||
from tractor.log import get_logger
|
||||
|
||||
|
@ -75,8 +75,8 @@ class DiffDump(UserList):
|
|||
for k, left, right in self:
|
||||
repstr += (
|
||||
f'({k},\n'
|
||||
f'\t{repr(left)},\n'
|
||||
f'\t{repr(right)},\n'
|
||||
f' |_{repr(left)},\n'
|
||||
f' |_{repr(right)},\n'
|
||||
')\n'
|
||||
)
|
||||
repstr += ']\n'
|
||||
|
@ -144,15 +144,22 @@ def pformat(
|
|||
field_indent=indent + field_indent,
|
||||
)
|
||||
|
||||
else: # the `pprint` recursion-safe format:
|
||||
else:
|
||||
val_str: str = repr(v)
|
||||
|
||||
# XXX LOL, below just seems to be f#$%in causing
|
||||
# recursion errs..
|
||||
#
|
||||
# the `pprint` recursion-safe format:
|
||||
# https://docs.python.org/3.11/library/pprint.html#pprint.saferepr
|
||||
try:
|
||||
val_str: str = saferepr(v)
|
||||
except Exception:
|
||||
log.exception(
|
||||
'Failed to `saferepr({type(struct)})` !?\n'
|
||||
)
|
||||
return _Struct.__repr__(struct)
|
||||
# try:
|
||||
# val_str: str = saferepr(v)
|
||||
# except Exception:
|
||||
# log.exception(
|
||||
# 'Failed to `saferepr({type(struct)})` !?\n'
|
||||
# )
|
||||
# raise
|
||||
# return _Struct.__repr__(struct)
|
||||
|
||||
# TODO: LOLOL use `textwrap.indent()` instead dawwwwwg!
|
||||
obj_str += (field_ws + f'{k}: {typ_name} = {val_str},\n')
|
||||
|
@ -203,12 +210,7 @@ class Struct(
|
|||
return sin_props
|
||||
|
||||
pformat = pformat
|
||||
# __repr__ = pformat
|
||||
# __str__ = __repr__ = pformat
|
||||
# TODO: use a pprint.PrettyPrinter instance around ONLY rendering
|
||||
# inside a known tty?
|
||||
# def __repr__(self) -> str:
|
||||
# ...
|
||||
|
||||
def __repr__(self) -> str:
|
||||
try:
|
||||
return pformat(self)
|
||||
|
@ -218,6 +220,13 @@ class Struct(
|
|||
)
|
||||
return _Struct.__repr__(self)
|
||||
|
||||
# __repr__ = pformat
|
||||
# __str__ = __repr__ = pformat
|
||||
# TODO: use a pprint.PrettyPrinter instance around ONLY rendering
|
||||
# inside a known tty?
|
||||
# def __repr__(self) -> str:
|
||||
# ...
|
||||
|
||||
def copy(
|
||||
self,
|
||||
update: dict | None = None,
|
||||
|
@ -267,13 +276,15 @@ class Struct(
|
|||
fi.type(getattr(self, fi.name)),
|
||||
)
|
||||
|
||||
# TODO: make a mod func instead and just point to it here for
|
||||
# method impl?
|
||||
def __sub__(
|
||||
self,
|
||||
other: Struct,
|
||||
|
||||
) -> DiffDump[tuple[str, Any, Any]]:
|
||||
'''
|
||||
Compare fields/items key-wise and return a ``DiffDump``
|
||||
Compare fields/items key-wise and return a `DiffDump`
|
||||
for easy visual REPL comparison B)
|
||||
|
||||
'''
|
||||
|
@ -290,3 +301,42 @@ class Struct(
|
|||
))
|
||||
|
||||
return diffs
|
||||
|
||||
@classmethod
|
||||
def fields_diff(
|
||||
cls,
|
||||
other: dict|Struct,
|
||||
|
||||
) -> DiffDump[tuple[str, Any, Any]]:
|
||||
'''
|
||||
Very similar to `PrettyStruct.__sub__()` except accepts an
|
||||
input `other: dict` (presumably that would normally be called
|
||||
like `Struct(**other)`) which returns a `DiffDump` of the
|
||||
fields of the struct and the `dict`'s fields.
|
||||
|
||||
'''
|
||||
nullish = object()
|
||||
consumed: dict = other.copy()
|
||||
diffs: DiffDump[tuple[str, Any, Any]] = DiffDump()
|
||||
for fi in structs.fields(cls):
|
||||
field_name: str = fi.name
|
||||
# ours: Any = getattr(self, field_name)
|
||||
theirs: Any = consumed.pop(field_name, nullish)
|
||||
if theirs is nullish:
|
||||
diffs.append((
|
||||
field_name,
|
||||
f'{fi.type!r}',
|
||||
'NOT-DEFINED in `other: dict`',
|
||||
))
|
||||
|
||||
# when there are lingering fields in `other` that this struct
|
||||
# DOES NOT define we also append those.
|
||||
if consumed:
|
||||
for k, v in consumed.items():
|
||||
diffs.append((
|
||||
k,
|
||||
f'NOT-DEFINED for `{cls.__name__}`',
|
||||
f'`other: dict` has value = {v!r}',
|
||||
))
|
||||
|
||||
return diffs
|
||||
|
|
|
@ -245,14 +245,14 @@ def _run_asyncio_task(
|
|||
result != orig and
|
||||
aio_err is None and
|
||||
|
||||
# in the ``open_channel_from()`` case we don't
|
||||
# in the `open_channel_from()` case we don't
|
||||
# relay through the "return value".
|
||||
not provide_channels
|
||||
):
|
||||
to_trio.send_nowait(result)
|
||||
|
||||
finally:
|
||||
# if the task was spawned using ``open_channel_from()``
|
||||
# if the task was spawned using `open_channel_from()`
|
||||
# then we close the channels on exit.
|
||||
if provide_channels:
|
||||
# only close the sender side which will relay
|
||||
|
@ -500,7 +500,7 @@ async def run_task(
|
|||
|
||||
'''
|
||||
# simple async func
|
||||
chan = _run_asyncio_task(
|
||||
chan: LinkedTaskChannel = _run_asyncio_task(
|
||||
func,
|
||||
qsize=1,
|
||||
**kwargs,
|
||||
|
@ -530,7 +530,7 @@ async def open_channel_from(
|
|||
spawned ``asyncio`` task and ``trio``.
|
||||
|
||||
'''
|
||||
chan = _run_asyncio_task(
|
||||
chan: LinkedTaskChannel = _run_asyncio_task(
|
||||
target,
|
||||
qsize=2**8,
|
||||
provide_channels=True,
|
||||
|
|
|
@ -382,7 +382,7 @@ class BroadcastReceiver(ReceiveChannel):
|
|||
# likely it makes sense to unwind back to the
|
||||
# underlying?
|
||||
# import tractor
|
||||
# await tractor.breakpoint()
|
||||
# await tractor.pause()
|
||||
log.warning(
|
||||
f'Only one sub left for {self}?\n'
|
||||
'We can probably unwind from breceiver?'
|
||||
|
|
Loading…
Reference in New Issue