tuxbot-bot/venv/lib/python3.7/site-packages/jishaku/repl/scope.py

172 lines
4.2 KiB
Python
Raw Normal View History

2019-12-16 18:12:10 +01:00
# -*- coding: utf-8 -*-
"""
jishaku.repl.scope
~~~~~~~~~~~~~~~~~~
The Scope class and functions relating to it.
:copyright: (c) 2019 Devon (Gorialis) R
:license: MIT, see LICENSE for more details.
"""
import inspect
import typing
class Scope:
"""
Class that represents a global and local scope for both scope inspection and creation.
Many REPL functions expect or return this class.
.. code:: python3
scope = Scope() # an empty Scope
scope = Scope(globals(), locals()) # a Scope imitating the current, real scope.
scope = Scope({'a': 3}) # a Scope with a pre-existing global scope key, and an empty local scope.
"""
__slots__ = ('globals', 'locals')
def __init__(self, globals_: dict = None, locals_: dict = None):
self.globals: dict = globals_ or {}
self.locals: dict = locals_ or {}
def clear_intersection(self, other_dict):
"""
Clears out locals and globals from this scope where the key-value pair matches
with other_dict.
This allows cleanup of temporary variables that may have washed up into this
Scope.
Parameters
-----------
other_dict: :class:`dict`
The dictionary to be used to determine scope clearance.
If a key from this dict matches an entry in the globals or locals of this scope,
and the value is identical, it is removed from the scope.
Returns
-------
Scope
The updated scope (self).
"""
for key, value in other_dict.items():
if key in self.globals and self.globals[key] is value:
del self.globals[key]
if key in self.locals and self.locals[key] is value:
del self.locals[key]
return self
def update(self, other):
"""
Updates this scope with the content of another scope.
Parameters
---------
other: :class:`Scope`
The scope to overlay onto this one.
Returns
-------
Scope
The updated scope (self).
"""
self.globals.update(other.globals)
self.locals.update(other.locals)
return self
def update_globals(self, other: dict):
"""
Updates this scope's globals with a dict.
Parameters
-----------
other: :class:`dict`
The dictionary to be merged into this scope.
Returns
-------
Scope
The updated scope (self).
"""
self.globals.update(other)
return self
def update_locals(self, other: dict):
"""
Updates this scope's locals with a dict.
Parameters
-----------
other: :class:`dict`
The dictionary to be merged into this scope.
Returns
-------
Scope
The updated scope (self).
"""
self.locals.update(other)
return self
def get_parent_scope_from_var(name, global_ok=False, skip_frames=0) -> typing.Optional[Scope]:
"""
Iterates up the frame stack looking for a frame-scope containing the given variable name.
Returns
--------
Optional[Scope]
The relevant :class:`Scope` or None
"""
stack = inspect.stack()
try:
for frame_info in stack[skip_frames + 1:]:
frame = None
try:
frame = frame_info.frame
if name in frame.f_locals or (global_ok and name in frame.f_globals):
return Scope(globals_=frame.f_globals, locals_=frame.f_locals)
finally:
del frame
finally:
del stack
return None
def get_parent_var(name, global_ok=False, default=None, skip_frames=0):
"""
Directly gets a variable from a parent frame-scope.
Returns
--------
Any
The content of the variable found by the given name, or None.
"""
scope = get_parent_scope_from_var(name, global_ok=global_ok, skip_frames=skip_frames + 1)
if not scope:
return default
if name in scope.locals:
return scope.locals.get(name, default)
return scope.globals.get(name, default)