Some of the stuff edited (`disco.util.config.Config`) are not within the scope of this PR, but `disco.util.runner.create_bot` makes it easier to mess up.
(also, if lines 414 to 417 are premature optimization, that's all me)
This commit is a fairly large chunk of code that fixes a previously
annoying and nasty bug causing cached_properties to not be cleared.
Alongside this bug fix I took the opportunity to refactor the entire
methdology behind cached properties, and bind them more strictly to the
behavior of models.
Prior to this commit, cached_properties where not be properly reset upon
a model's `update` method being called due to some invalid code. Along
with this issue, the actual behavior of cached properties landed in a
weird in-between/gray area where they had to support both non-model and
model use cases.
The new version of cached_property is strictly for use with models, and
another version `simple_cached_property` was added to support other use
cases. The new cached_property simply builds an actual `property` within
the metaclass. This is fairly efficient, and also reduces the surface
area of behavior.
When writing this I messed around with a few ideas, including allowing
the user of `cached_property` to define a linkage between the property
and fields that should invalidate its cached value, but this was both
messy and introduced massive cognitive load on understand when a
cached_property should be cleared. Although this version is slighty less
efficient, I'm very much in favor of it vs the alternatives I tried.
UserDict was the wrong way to achieve this, modern versions of Python
can just subclass dict. This provides an immense performance boost by
allowing getitem/setitem calls to be routed directly to the underlying
storage within cpython land, instead of having to route through the
items MRO and eventually hit __dict__
This would cause our GatewayClient to get rate limited when guild member
syncing was enabled for clients with a large number of guilds. The bug
had to do with the limiter releaseing all the requests after the initial
period it blocked for. This was resolved by rewriting the SimpleLimiter
to use a semaphore.
This naming scheme was garbage and always tripped me up. In favor of
having a more intuitive (and less _technically_ correct) interface,
we'll swap the name up.
Tl;dr we now use __slots__ in a bunch of places. This could still be
better, and we do a bit too much magic in the modeling to make me happy.
But thats for later, for now we're going from ~250mb on 2500 guilds to
~160mb.
- Allow configuring the state module within the normal configuration
(under the 'state' key)
- Rate limit events being sent on the gateway socket
- Convert to using lazy_datetime in a bunch of places
- Allow configuring guild member sync
- Better logic around loading guilds, add State.ready condition which
can be waited on
- Fix inheritance in the modeling framework (how was this not working
before lol wut)
- Added __slots__ to a bunch of low-hanging fruit models
- Move member sync onto the guild object as Guild.sync()
- Convert to Dannys CachedSlotProperty (could still be better, will
improve later)
- Added util.snowflake.calculate_shard
The biggest part of this commit is a plugin storage subsystem, which at
this point I'm fairly happy with. I've iterated on this a couple times,
and the final result has a very clean/simple interface, is easy to
extend to different data stores, and has a very few minimal number of
grokable edge cases.
- Storage subsytem
- Fix command group abbreviations
- Fix reconnecting in the GatewaySocket
- Add pickle support to serializer