2022-05-23 00:16:32 +04:00

208 lines
5.8 KiB
Python

# Copyright 2018-2022 Streamlit Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Optional, Tuple, Any, List
from streamlit import util
from streamlit.scriptrunner import get_script_run_ctx
def make_delta_path(
root_container: int, parent_path: Tuple[int, ...], index: int
) -> List[int]:
delta_path = [root_container]
delta_path.extend(parent_path)
delta_path.append(index)
return delta_path
def get_container_cursor(
root_container: Optional[int],
) -> Optional["RunningCursor"]:
"""Return the top-level RunningCursor for the given container.
This is the cursor that is used when user code calls something like
`st.foo` (which uses the main container) or `st.sidebar.foo` (which uses
the sidebar container).
"""
if root_container is None:
return None
ctx = get_script_run_ctx()
if ctx is None:
return None
if root_container in ctx.cursors:
return ctx.cursors[root_container]
cursor = RunningCursor(root_container=root_container)
ctx.cursors[root_container] = cursor
return cursor
class Cursor:
"""A pointer to a delta location in the app.
When adding an element to the app, you should always call
get_locked_cursor() on that element's respective Cursor.
"""
def __repr__(self) -> str:
return util.repr_(self)
@property
def root_container(self) -> int:
"""The top-level container this cursor lives within - either
RootContainer.MAIN or RootContainer.SIDEBAR."""
raise NotImplementedError()
@property
def parent_path(self) -> Tuple[int, ...]:
"""The cursor's parent's path within its container."""
raise NotImplementedError()
@property
def index(self) -> int:
"""The index of the Delta within its parent block."""
raise NotImplementedError()
@property
def delta_path(self) -> List[int]:
"""The complete path of the delta pointed to by this cursor - its
container, parent path, and index.
"""
return make_delta_path(self.root_container, self.parent_path, self.index)
@property
def is_locked(self) -> bool:
raise NotImplementedError()
def get_locked_cursor(self, **props) -> "LockedCursor":
raise NotImplementedError()
@property
def props(self) -> Any:
"""Other data in this cursor. This is a temporary measure that will go
away when we implement improved return values for elements.
This is only implemented in LockedCursor.
"""
raise NotImplementedError()
class RunningCursor(Cursor):
def __init__(self, root_container: int, parent_path: Tuple[int, ...] = ()):
"""A moving pointer to a delta location in the app.
RunningCursors auto-increment to the next available location when you
call get_locked_cursor() on them.
Parameters
----------
root_container: int
The root container this cursor lives in.
parent_path: tuple of ints
The full path of this cursor, consisting of the IDs of all ancestors.
The 0th item is the topmost ancestor.
"""
self._root_container = root_container
self._parent_path = parent_path
self._index = 0
@property
def root_container(self) -> int:
return self._root_container
@property
def parent_path(self) -> Tuple[int, ...]:
return self._parent_path
@property
def index(self) -> int:
return self._index
@property
def is_locked(self) -> bool:
return False
def get_locked_cursor(self, **props) -> "LockedCursor":
locked_cursor = LockedCursor(
root_container=self._root_container,
parent_path=self._parent_path,
index=self._index,
**props,
)
self._index += 1
return locked_cursor
class LockedCursor(Cursor):
def __init__(
self,
root_container: int,
parent_path: Tuple[int, ...] = (),
index: int = 0,
**props,
):
"""A locked pointer to a location in the app.
LockedCursors always point to the same location, even when you call
get_locked_cursor() on them.
Parameters
----------
root_container: int
The root container this cursor lives in.
parent_path: tuple of ints
The full path of this cursor, consisting of the IDs of all ancestors. The
0th item is the topmost ancestor.
index: int
**props: any
Anything else you want to store in this cursor. This is a temporary
measure that will go away when we implement improved return values
for elements.
"""
self._root_container = root_container
self._index = index
self._parent_path = parent_path
self._props = props
@property
def root_container(self) -> int:
return self._root_container
@property
def parent_path(self) -> Tuple[int, ...]:
return self._parent_path
@property
def index(self) -> int:
return self._index
@property
def is_locked(self) -> bool:
return True
def get_locked_cursor(self, **props) -> "LockedCursor":
self._props = props
return self
@property
def props(self) -> Any:
return self._props