Skip to content

Commit

Permalink
Lazy-load vendored pympler asizeof module (#8124)
Browse files Browse the repository at this point in the history
## Describe your changes

The vendored pympler module is only used when someone explicitly
requests the metrics via the metrics endpoint. This PR moves the module
to lazyloading.

## GitHub Issue Link (if applicable)

Related to #6066

## Testing Plan

- Added e2e test to ensure that vendored `pympler` module is lazy
loaded.
---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
  • Loading branch information
LukasMasuch committed Feb 9, 2024
1 parent fdf54e3 commit 5a6c331
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 10 deletions.
9 changes: 7 additions & 2 deletions e2e_playwright/lazy_loaded_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
"watchdog",
"streamlit.emojis",
"streamlit.external",
"streamlit.vendor.pympler",
"streamlit.watcher.event_based_path_watcher",
# TODO(lukasmasuch): Lazy load more packages:
# "streamlit.hello",
# "streamlit.vendor.pympler",
# "pandas",
# "pyarrow",
# "numpy",
Expand All @@ -41,4 +41,9 @@

for module in lazy_loaded_modules:
loaded = module in sys.modules
st.write(f"**{module}**:", ("loaded" if loaded else "not loaded"))
st.write(f"**{module}**:", ("imported" if loaded else "not loaded"))

if st.button("Import lazy loaded modules"):
for module in lazy_loaded_modules:
__import__(module)
st.rerun()
2 changes: 1 addition & 1 deletion e2e_playwright/lazy_loaded_modules_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
def test_lazy_loaded_modules_are_not_imported(app: Page):
"""Test that lazy loaded modules are not imported when the page is loaded."""
markdown_elements = app.get_by_test_id("stMarkdown")
expect(markdown_elements).to_have_count(10)
expect(markdown_elements).to_have_count(11)
for element in markdown_elements.all():
expect(element).to_have_text(re.compile(r".*not loaded.*"))
8 changes: 5 additions & 3 deletions lib/streamlit/runtime/caching/cache_resource_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import threading
import types
from datetime import timedelta
from typing import Any, Callable, TypeVar, cast, overload
from typing import Any, Callable, Final, TypeVar, cast, overload

from typing_extensions import TypeAlias

Expand Down Expand Up @@ -48,9 +48,8 @@
from streamlit.runtime.scriptrunner.script_run_context import get_script_run_ctx
from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
from streamlit.util import TimedCleanupCache
from streamlit.vendor.pympler.asizeof import asizeof

_LOGGER = get_logger(__name__)
_LOGGER: Final = get_logger(__name__)


CACHE_RESOURCE_MESSAGE_REPLAY_CTX = CachedMessageReplayContext(CacheType.RESOURCE)
Expand Down Expand Up @@ -561,6 +560,9 @@ def get_stats(self) -> list[CacheStat]:
with self._mem_cache_lock:
cache_entries = list(self._mem_cache.values())

# Lazy-load vendored package to prevent import of numpy
from streamlit.vendor.pympler.asizeof import asizeof

return [
CacheStat(
category_name="st_cache_resource",
Expand Down
7 changes: 5 additions & 2 deletions lib/streamlit/runtime/legacy_caching/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
Any,
Callable,
Dict,
Final,
Iterator,
List,
Optional,
Expand Down Expand Up @@ -58,9 +59,8 @@
from streamlit.runtime.metrics_util import gather_metrics
from streamlit.runtime.stats import CacheStat, CacheStatsProvider
from streamlit.util import HASHLIB_KWARGS
from streamlit.vendor.pympler.asizeof import asizeof

_LOGGER = get_logger(__name__)
_LOGGER: Final = get_logger(__name__)

# The timer function we use with TTLCache. This is the default timer func, but
# is exposed here as a constant so that it can be patched in unit tests.
Expand Down Expand Up @@ -226,6 +226,9 @@ def get_stats(self) -> List[CacheStat]:
# lock during stats-gathering.
function_caches = self._function_caches.copy()

# Lazy-load vendored package to prevent import of numpy
from streamlit.vendor.pympler.asizeof import asizeof

stats = [
CacheStat("st_cache", cache.display_name, asizeof(c))
for cache in function_caches.values()
Expand Down
8 changes: 6 additions & 2 deletions lib/streamlit/runtime/state/session_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# 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 __future__ import annotations

import json
Expand All @@ -20,6 +21,7 @@
from typing import (
TYPE_CHECKING,
Any,
Final,
Iterator,
KeysView,
List,
Expand All @@ -28,7 +30,7 @@
cast,
)

from typing_extensions import Final, TypeAlias
from typing_extensions import TypeAlias

import streamlit as st
from streamlit import config, util
Expand All @@ -45,7 +47,6 @@
from streamlit.runtime.state.query_params import QueryParams
from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
from streamlit.type_util import ValueFieldName, is_array_value_field_name
from streamlit.vendor.pympler.asizeof import asizeof

if TYPE_CHECKING:
from streamlit.runtime.session_manager import SessionManager
Expand Down Expand Up @@ -637,6 +638,9 @@ def __contains__(self, key: str) -> bool:
return True

def get_stats(self) -> list[CacheStat]:
# Lazy-load vendored package to prevent import of numpy
from streamlit.vendor.pympler.asizeof import asizeof

stat = CacheStat("st_session_state", "", asizeof(self))
return [stat]

Expand Down

0 comments on commit 5a6c331

Please sign in to comment.