Add a proper dynamicscope iterator#571
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #571 +/- ##
==========================================
+ Coverage 70.92% 70.96% +0.03%
==========================================
Files 287 288 +1
Lines 25698 25733 +35
Branches 3641 3645 +4
==========================================
+ Hits 18226 18261 +35
Misses 6384 6384
Partials 1088 1088 🚀 New features to boost your workflow:
|
68e1c63 to
e666cd3
Compare
|
Do we agree this supersedes #566 ? |
|
Yes, should I close that or push this on top of that branch? |
|
You can close it, I just wanted to be sure. |
|
I think most of this should be done in a Python subclass of from itertools import chain, repeat
from _dynamicscope import _DynamicScope
"""
_DynamicScope is a C++ class which exposes the following attributes:
_f_writes
_self_str
_change_str
_f_locals
_f_globals
_f_builtins
"""
class DynamicScope(_DynamicScope):
def __iter__(self):
fwrites_it = iter(self._f_writes)
self_it = repeat(self._self_str, 1)
change_it = repeat(self._change_str, 1)
flocals_it = iter(self._f_locals)
fglobals_it = iter(self._f_globals)
fbuiltins_it = iter(self._f_builtins)
parent_it = iter(self._parent)
return chain(
fwrites_it,
self_it,
change_it,
flocals_it,
fglobals_it,
fbuiltins_it,
parent_it
)
def keys(self):
return iter(self)
def values(self):
return (self[key] for key in self)
def items(self):
return ((key, self[key]) for key in self)
def update(self, items):
for (key, value) in items:
self[key] = value |
|
Is it ok to expose the internal fields as properties? I guess it'd be fine with only getters. |
|
Yes. Make them read-only properties with a leading underscore. i.e. they
are "just an implementation detail" that the public shouldn't mess with.
…On Thu, Jan 30, 2025 at 2:14 PM frmdstryr ***@***.***> wrote:
Is it ok to expose the internal fields as properties? I guess it'd be fine
with only getters.
—
Reply to this email directly, view it on GitHub
<#571 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABBQSNQJBZQWWU4X3R3RZL2NKB3BAVCNFSM6AAAAABWDFZNF2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMRVGQ4DGNJXGA>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
e666cd3 to
7d96554
Compare
enaml/core/dynamicscope.py
Outdated
| fbuiltins_it = iter(self._f_builtins) | ||
| fields = set() | ||
| parent = self._owner | ||
| while parent is not None: |
There was a problem hiding this comment.
Why traverse the parent tree here ahead-of-time instead of allowing chain to naturally recurse into the parent iterator as needed?
There was a problem hiding this comment.
Not sure I follow... the owner is a Declarative, the parent is another Declarative neither are iterable. It could use traverse_ancestors?
Since the traceback module uses list(frame.f_locals), if it doesn't remove duplicates it will likely quickly overflow the 750 item limit and not provide a suggestion at all...
There was a problem hiding this comment.
Or is there a way to get the parent dynamic scope? I don't see it anywhere from a quick look.
There was a problem hiding this comment.
Sorry, yeah, I had it in my head that parent was another DynamicScope object. That said, I think we still put all of that logic into a generator function, instead of computing ahead-of-time.
There was a problem hiding this comment.
But yeah, traverse_ancestors could part of a generator function that aggregates the keys while only yielding newly-seen keys.
There was a problem hiding this comment.
For the 750 item limit, that really should be an issued raised with the core traceback module. Instead of 100% punting at 751 names, they should truncate their search space to the first 750 items, and just ignore the rest.
7d96554 to
5b9f191
Compare
enaml/core/dynamicscope.py
Outdated
| def items(self): | ||
| return ((key, self[key]) for key in self) | ||
|
|
||
| def update(self, state): |
There was a problem hiding this comment.
Do we need an update method? All the rest of the methods are readonly. I know the C++ class implements __setitem__, but IIRC that's in order to handle local assignments. I'm not sure we really need this method.
There was a problem hiding this comment.
Technically no... I added it to support ipython embedding since it attempts to update the scope when exiting an embedded console.
5b9f191 to
3adc245
Compare
enaml/core/dynamicscope.py
Outdated
| fglobals_it, | ||
| fbuiltins_it, | ||
| fields_it, | ||
| ), |
enaml/core/dynamicscope.py
Outdated
| result: bool | ||
| Whether the key should be included. | ||
| """ | ||
| if key.startswith("__") or key in used: |
There was a problem hiding this comment.
Should filter all keys with a single leading _ instead of just double? i.e. only yield the "public" keys.
3adc245 to
07fedc2
Compare
It is not very efficient to the use of dir() and a set to track used keys... but makes python 3.12+ provide useful messages for name errors and attribute errors.
I'd like to add more tests after any initial feedback.
Eg:
Gives
NameError: name 'windo' is not defined. Did you mean: 'window'?From looking at the traceback source code it ignores giving suggestions if there are > 750 items (https://github.com/python/cpython/blob/main/Lib/traceback.py#L1465). Which I can see it easily exceeding.. So it might be good to also skip globals and builtins?