ob-metaflow 2.11.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metaflow/R.py +10 -7
- metaflow/__init__.py +40 -25
- metaflow/_vendor/imghdr/__init__.py +186 -0
- metaflow/_vendor/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/typeguard/__init__.py +48 -0
- metaflow/_vendor/typeguard/_checkers.py +1070 -0
- metaflow/_vendor/typeguard/_config.py +108 -0
- metaflow/_vendor/typeguard/_decorators.py +233 -0
- metaflow/_vendor/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/typeguard/_functions.py +308 -0
- metaflow/_vendor/typeguard/_importhook.py +213 -0
- metaflow/_vendor/typeguard/_memo.py +48 -0
- metaflow/_vendor/typeguard/_pytest_plugin.py +127 -0
- metaflow/_vendor/typeguard/_suppression.py +86 -0
- metaflow/_vendor/typeguard/_transformer.py +1229 -0
- metaflow/_vendor/typeguard/_union_transformer.py +55 -0
- metaflow/_vendor/typeguard/_utils.py +173 -0
- metaflow/_vendor/typeguard/py.typed +0 -0
- metaflow/_vendor/typing_extensions.py +3641 -0
- metaflow/_vendor/v3_7/importlib_metadata/__init__.py +1063 -0
- metaflow/_vendor/v3_7/importlib_metadata/_adapters.py +68 -0
- metaflow/_vendor/v3_7/importlib_metadata/_collections.py +30 -0
- metaflow/_vendor/v3_7/importlib_metadata/_compat.py +71 -0
- metaflow/_vendor/v3_7/importlib_metadata/_functools.py +104 -0
- metaflow/_vendor/v3_7/importlib_metadata/_itertools.py +73 -0
- metaflow/_vendor/v3_7/importlib_metadata/_meta.py +48 -0
- metaflow/_vendor/v3_7/importlib_metadata/_text.py +99 -0
- metaflow/_vendor/v3_7/importlib_metadata/py.typed +0 -0
- metaflow/_vendor/v3_7/typeguard/__init__.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_checkers.py +906 -0
- metaflow/_vendor/v3_7/typeguard/_config.py +108 -0
- metaflow/_vendor/v3_7/typeguard/_decorators.py +237 -0
- metaflow/_vendor/v3_7/typeguard/_exceptions.py +42 -0
- metaflow/_vendor/v3_7/typeguard/_functions.py +310 -0
- metaflow/_vendor/v3_7/typeguard/_importhook.py +213 -0
- metaflow/_vendor/v3_7/typeguard/_memo.py +48 -0
- metaflow/_vendor/v3_7/typeguard/_pytest_plugin.py +100 -0
- metaflow/_vendor/v3_7/typeguard/_suppression.py +88 -0
- metaflow/_vendor/v3_7/typeguard/_transformer.py +1207 -0
- metaflow/_vendor/v3_7/typeguard/_union_transformer.py +54 -0
- metaflow/_vendor/v3_7/typeguard/_utils.py +169 -0
- metaflow/_vendor/v3_7/typeguard/py.typed +0 -0
- metaflow/_vendor/v3_7/typing_extensions.py +3072 -0
- metaflow/_vendor/yaml/__init__.py +427 -0
- metaflow/_vendor/yaml/composer.py +139 -0
- metaflow/_vendor/yaml/constructor.py +748 -0
- metaflow/_vendor/yaml/cyaml.py +101 -0
- metaflow/_vendor/yaml/dumper.py +62 -0
- metaflow/_vendor/yaml/emitter.py +1137 -0
- metaflow/_vendor/yaml/error.py +75 -0
- metaflow/_vendor/yaml/events.py +86 -0
- metaflow/_vendor/yaml/loader.py +63 -0
- metaflow/_vendor/yaml/nodes.py +49 -0
- metaflow/_vendor/yaml/parser.py +589 -0
- metaflow/_vendor/yaml/reader.py +185 -0
- metaflow/_vendor/yaml/representer.py +389 -0
- metaflow/_vendor/yaml/resolver.py +227 -0
- metaflow/_vendor/yaml/scanner.py +1435 -0
- metaflow/_vendor/yaml/serializer.py +111 -0
- metaflow/_vendor/yaml/tokens.py +104 -0
- metaflow/cards.py +5 -0
- metaflow/cli.py +331 -785
- metaflow/cli_args.py +17 -0
- metaflow/cli_components/__init__.py +0 -0
- metaflow/cli_components/dump_cmd.py +96 -0
- metaflow/cli_components/init_cmd.py +52 -0
- metaflow/cli_components/run_cmds.py +546 -0
- metaflow/cli_components/step_cmd.py +334 -0
- metaflow/cli_components/utils.py +140 -0
- metaflow/client/__init__.py +1 -0
- metaflow/client/core.py +467 -73
- metaflow/client/filecache.py +75 -35
- metaflow/clone_util.py +7 -1
- metaflow/cmd/code/__init__.py +231 -0
- metaflow/cmd/develop/stub_generator.py +756 -288
- metaflow/cmd/develop/stubs.py +12 -28
- metaflow/cmd/main_cli.py +6 -4
- metaflow/cmd/make_wrapper.py +78 -0
- metaflow/datastore/__init__.py +1 -0
- metaflow/datastore/content_addressed_store.py +41 -10
- metaflow/datastore/datastore_set.py +11 -2
- metaflow/datastore/flow_datastore.py +156 -10
- metaflow/datastore/spin_datastore.py +91 -0
- metaflow/datastore/task_datastore.py +154 -39
- metaflow/debug.py +5 -0
- metaflow/decorators.py +404 -78
- metaflow/exception.py +8 -2
- metaflow/extension_support/__init__.py +527 -376
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/extension_support/plugins.py +49 -31
- metaflow/flowspec.py +482 -33
- metaflow/graph.py +210 -42
- metaflow/includefile.py +84 -40
- metaflow/lint.py +141 -22
- metaflow/meta_files.py +13 -0
- metaflow/{metadata → metadata_provider}/heartbeat.py +24 -8
- metaflow/{metadata → metadata_provider}/metadata.py +86 -1
- metaflow/metaflow_config.py +175 -28
- metaflow/metaflow_config_funcs.py +51 -3
- metaflow/metaflow_current.py +4 -10
- metaflow/metaflow_environment.py +139 -53
- metaflow/metaflow_git.py +115 -0
- metaflow/metaflow_profile.py +18 -0
- metaflow/metaflow_version.py +150 -66
- metaflow/mflog/__init__.py +4 -3
- metaflow/mflog/save_logs.py +2 -2
- metaflow/multicore_utils.py +31 -14
- metaflow/package/__init__.py +673 -0
- metaflow/packaging_sys/__init__.py +880 -0
- metaflow/packaging_sys/backend.py +128 -0
- metaflow/packaging_sys/distribution_support.py +153 -0
- metaflow/packaging_sys/tar_backend.py +99 -0
- metaflow/packaging_sys/utils.py +54 -0
- metaflow/packaging_sys/v1.py +527 -0
- metaflow/parameters.py +149 -28
- metaflow/plugins/__init__.py +74 -5
- metaflow/plugins/airflow/airflow.py +40 -25
- metaflow/plugins/airflow/airflow_cli.py +22 -5
- metaflow/plugins/airflow/airflow_decorator.py +1 -1
- metaflow/plugins/airflow/airflow_utils.py +5 -3
- metaflow/plugins/airflow/sensors/base_sensor.py +4 -4
- metaflow/plugins/airflow/sensors/external_task_sensor.py +2 -2
- metaflow/plugins/airflow/sensors/s3_sensor.py +2 -2
- metaflow/plugins/argo/argo_client.py +78 -33
- metaflow/plugins/argo/argo_events.py +6 -6
- metaflow/plugins/argo/argo_workflows.py +2410 -527
- metaflow/plugins/argo/argo_workflows_cli.py +571 -121
- metaflow/plugins/argo/argo_workflows_decorator.py +43 -12
- metaflow/plugins/argo/argo_workflows_deployer.py +106 -0
- metaflow/plugins/argo/argo_workflows_deployer_objects.py +453 -0
- metaflow/plugins/argo/capture_error.py +73 -0
- metaflow/plugins/argo/conditional_input_paths.py +35 -0
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/argo/jobset_input_paths.py +15 -0
- metaflow/plugins/argo/param_val.py +19 -0
- metaflow/plugins/aws/aws_client.py +10 -3
- metaflow/plugins/aws/aws_utils.py +55 -2
- metaflow/plugins/aws/batch/batch.py +72 -5
- metaflow/plugins/aws/batch/batch_cli.py +33 -10
- metaflow/plugins/aws/batch/batch_client.py +4 -3
- metaflow/plugins/aws/batch/batch_decorator.py +102 -35
- metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +13 -10
- metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -3
- metaflow/plugins/aws/step_functions/production_token.py +1 -1
- metaflow/plugins/aws/step_functions/step_functions.py +65 -8
- metaflow/plugins/aws/step_functions/step_functions_cli.py +101 -7
- metaflow/plugins/aws/step_functions/step_functions_decorator.py +1 -2
- metaflow/plugins/aws/step_functions/step_functions_deployer.py +97 -0
- metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +264 -0
- metaflow/plugins/azure/azure_exceptions.py +1 -1
- metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +240 -0
- metaflow/plugins/azure/azure_tail.py +1 -1
- metaflow/plugins/azure/includefile_support.py +2 -0
- metaflow/plugins/cards/card_cli.py +66 -30
- metaflow/plugins/cards/card_creator.py +25 -1
- metaflow/plugins/cards/card_datastore.py +21 -49
- metaflow/plugins/cards/card_decorator.py +132 -8
- metaflow/plugins/cards/card_modules/basic.py +112 -17
- metaflow/plugins/cards/card_modules/bundle.css +1 -1
- metaflow/plugins/cards/card_modules/card.py +16 -1
- metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
- metaflow/plugins/cards/card_modules/components.py +665 -28
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +36 -7
- metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
- metaflow/plugins/cards/card_modules/main.css +1 -0
- metaflow/plugins/cards/card_modules/main.js +68 -49
- metaflow/plugins/cards/card_modules/renderer_tools.py +1 -0
- metaflow/plugins/cards/card_modules/test_cards.py +26 -12
- metaflow/plugins/cards/card_server.py +39 -14
- metaflow/plugins/cards/component_serializer.py +2 -9
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/catch_decorator.py +9 -0
- metaflow/plugins/datastores/azure_storage.py +10 -1
- metaflow/plugins/datastores/gs_storage.py +6 -2
- metaflow/plugins/datastores/local_storage.py +12 -6
- metaflow/plugins/datastores/spin_storage.py +12 -0
- metaflow/plugins/datatools/local.py +2 -0
- metaflow/plugins/datatools/s3/s3.py +126 -75
- metaflow/plugins/datatools/s3/s3op.py +254 -121
- metaflow/plugins/env_escape/__init__.py +3 -3
- metaflow/plugins/env_escape/client_modules.py +102 -72
- metaflow/plugins/env_escape/server.py +7 -0
- metaflow/plugins/env_escape/stub.py +24 -5
- metaflow/plugins/events_decorator.py +343 -185
- metaflow/plugins/exit_hook/__init__.py +0 -0
- metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
- metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
- metaflow/plugins/gcp/__init__.py +1 -1
- metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +11 -6
- metaflow/plugins/gcp/gs_tail.py +10 -6
- metaflow/plugins/gcp/includefile_support.py +3 -0
- metaflow/plugins/kubernetes/kube_utils.py +108 -0
- metaflow/plugins/kubernetes/kubernetes.py +411 -130
- metaflow/plugins/kubernetes/kubernetes_cli.py +168 -36
- metaflow/plugins/kubernetes/kubernetes_client.py +104 -2
- metaflow/plugins/kubernetes/kubernetes_decorator.py +246 -88
- metaflow/plugins/kubernetes/kubernetes_job.py +253 -581
- metaflow/plugins/kubernetes/kubernetes_jobsets.py +1071 -0
- metaflow/plugins/kubernetes/spot_metadata_cli.py +69 -0
- metaflow/plugins/kubernetes/spot_monitor_sidecar.py +109 -0
- metaflow/plugins/logs_cli.py +359 -0
- metaflow/plugins/{metadata → metadata_providers}/local.py +144 -84
- metaflow/plugins/{metadata → metadata_providers}/service.py +103 -26
- metaflow/plugins/metadata_providers/spin.py +16 -0
- metaflow/plugins/package_cli.py +36 -24
- metaflow/plugins/parallel_decorator.py +128 -11
- metaflow/plugins/parsers.py +16 -0
- metaflow/plugins/project_decorator.py +51 -5
- metaflow/plugins/pypi/bootstrap.py +357 -105
- metaflow/plugins/pypi/conda_decorator.py +82 -81
- metaflow/plugins/pypi/conda_environment.py +187 -52
- metaflow/plugins/pypi/micromamba.py +157 -47
- metaflow/plugins/pypi/parsers.py +268 -0
- metaflow/plugins/pypi/pip.py +88 -13
- metaflow/plugins/pypi/pypi_decorator.py +37 -1
- metaflow/plugins/pypi/utils.py +48 -2
- metaflow/plugins/resources_decorator.py +2 -2
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +26 -181
- metaflow/plugins/secrets/secrets_func.py +49 -0
- metaflow/plugins/secrets/secrets_spec.py +101 -0
- metaflow/plugins/secrets/utils.py +74 -0
- metaflow/plugins/tag_cli.py +4 -7
- metaflow/plugins/test_unbounded_foreach_decorator.py +41 -6
- metaflow/plugins/timeout_decorator.py +3 -3
- metaflow/plugins/uv/__init__.py +0 -0
- metaflow/plugins/uv/bootstrap.py +128 -0
- metaflow/plugins/uv/uv_environment.py +72 -0
- metaflow/procpoll.py +1 -1
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/__init__.py +0 -0
- metaflow/runner/click_api.py +717 -0
- metaflow/runner/deployer.py +470 -0
- metaflow/runner/deployer_impl.py +201 -0
- metaflow/runner/metaflow_runner.py +714 -0
- metaflow/runner/nbdeploy.py +132 -0
- metaflow/runner/nbrun.py +225 -0
- metaflow/runner/subprocess_manager.py +650 -0
- metaflow/runner/utils.py +335 -0
- metaflow/runtime.py +1078 -260
- metaflow/sidecar/sidecar_worker.py +1 -1
- metaflow/system/__init__.py +5 -0
- metaflow/system/system_logger.py +85 -0
- metaflow/system/system_monitor.py +108 -0
- metaflow/system/system_utils.py +19 -0
- metaflow/task.py +521 -225
- metaflow/tracing/__init__.py +7 -7
- metaflow/tracing/span_exporter.py +31 -38
- metaflow/tracing/tracing_modules.py +38 -43
- metaflow/tuple_util.py +27 -0
- metaflow/user_configs/__init__.py +0 -0
- metaflow/user_configs/config_options.py +563 -0
- metaflow/user_configs/config_parameters.py +598 -0
- metaflow/user_decorators/__init__.py +0 -0
- metaflow/user_decorators/common.py +144 -0
- metaflow/user_decorators/mutable_flow.py +512 -0
- metaflow/user_decorators/mutable_step.py +424 -0
- metaflow/user_decorators/user_flow_decorator.py +264 -0
- metaflow/user_decorators/user_step_decorator.py +749 -0
- metaflow/util.py +243 -27
- metaflow/vendor.py +23 -7
- metaflow/version.py +1 -1
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Makefile +355 -0
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/Tiltfile +726 -0
- ob_metaflow-2.19.7.1rc0.data/data/share/metaflow/devtools/pick_services.sh +105 -0
- ob_metaflow-2.19.7.1rc0.dist-info/METADATA +87 -0
- ob_metaflow-2.19.7.1rc0.dist-info/RECORD +445 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +1 -0
- metaflow/_vendor/v3_5/__init__.py +0 -1
- metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
- metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
- metaflow/package.py +0 -188
- ob_metaflow-2.11.13.1.dist-info/METADATA +0 -85
- ob_metaflow-2.11.13.1.dist-info/RECORD +0 -308
- /metaflow/_vendor/{v3_5/zipp.py → zipp.py} +0 -0
- /metaflow/{metadata → metadata_provider}/__init__.py +0 -0
- /metaflow/{metadata → metadata_provider}/util.py +0 -0
- /metaflow/plugins/{metadata → metadata_providers}/__init__.py +0 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info/licenses}/LICENSE +0 -0
- {ob_metaflow-2.11.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
|
@@ -8,7 +8,9 @@ TypeResolvedObject = namedtuple("TypeResolvedObject", ["data", "is_image", "is_t
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
TIME_FORMAT = "%Y-%m-%d %I:%M:%S %p"
|
|
11
|
-
|
|
11
|
+
# Maximum artifact size to render in cards: 200MB (in bytes)
|
|
12
|
+
# Artifacts larger than this will be skipped during card rendering to avoid memory issues
|
|
13
|
+
MAX_ARTIFACT_SIZE = 256 * 1024 * 1024 # 256 MB = 268435456 bytes
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def _get_object_size(obj, seen=None):
|
|
@@ -44,7 +46,7 @@ def _full_classname(obj):
|
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
class TaskToDict:
|
|
47
|
-
def __init__(self, only_repr=False, runtime=False):
|
|
49
|
+
def __init__(self, only_repr=False, runtime=False, max_artifact_size=None):
|
|
48
50
|
# this dictionary holds all the supported functions
|
|
49
51
|
import reprlib
|
|
50
52
|
import pprint
|
|
@@ -61,6 +63,10 @@ class TaskToDict:
|
|
|
61
63
|
self._repr = r
|
|
62
64
|
self._runtime = runtime
|
|
63
65
|
self._only_repr = only_repr
|
|
66
|
+
# Use the global MAX_ARTIFACT_SIZE constant if not specified
|
|
67
|
+
self._max_artifact_size = (
|
|
68
|
+
max_artifact_size if max_artifact_size is not None else MAX_ARTIFACT_SIZE
|
|
69
|
+
)
|
|
64
70
|
self._supported_types = {
|
|
65
71
|
"tuple": self._parse_tuple,
|
|
66
72
|
"NoneType": self._parse_nonetype,
|
|
@@ -110,6 +116,19 @@ class TaskToDict:
|
|
|
110
116
|
task_data_dict = {}
|
|
111
117
|
type_inferred_objects = {"images": {}, "tables": {}}
|
|
112
118
|
for data in task:
|
|
119
|
+
# Check if artifact size exceeds the maximum allowed size
|
|
120
|
+
if data.size > self._max_artifact_size:
|
|
121
|
+
# Skip artifacts that are too large
|
|
122
|
+
task_data_dict[data.id] = dict(
|
|
123
|
+
type="skipped",
|
|
124
|
+
data=f"<artifact too large: {data.size} bytes, max: {self._max_artifact_size} bytes>",
|
|
125
|
+
large_object=True,
|
|
126
|
+
supported_type=False,
|
|
127
|
+
only_repr=self._only_repr,
|
|
128
|
+
name=data.id,
|
|
129
|
+
)
|
|
130
|
+
continue
|
|
131
|
+
|
|
113
132
|
try:
|
|
114
133
|
data_object = data.data
|
|
115
134
|
task_data_dict[data.id] = self._convert_to_native_type(data_object)
|
|
@@ -143,7 +162,16 @@ class TaskToDict:
|
|
|
143
162
|
obj_type_name = self._get_object_type(data_object)
|
|
144
163
|
if obj_type_name == "bytes":
|
|
145
164
|
# Works for python 3.1+
|
|
146
|
-
|
|
165
|
+
# Python 3.13 removes the standard ``imghdr`` module. Metaflow
|
|
166
|
+
# vendors a copy so we can keep using ``what`` to detect image
|
|
167
|
+
# formats irrespective of the Python version.
|
|
168
|
+
import warnings
|
|
169
|
+
|
|
170
|
+
with warnings.catch_warnings():
|
|
171
|
+
warnings.filterwarnings(
|
|
172
|
+
"ignore", category=DeprecationWarning, module="imghdr"
|
|
173
|
+
)
|
|
174
|
+
from metaflow._vendor import imghdr
|
|
147
175
|
|
|
148
176
|
resp = imghdr.what(None, h=data_object)
|
|
149
177
|
# Only accept types supported on the web
|
|
@@ -157,7 +185,7 @@ class TaskToDict:
|
|
|
157
185
|
obj_type_name = self._get_object_type(data_object)
|
|
158
186
|
if obj_type_name == "bytes":
|
|
159
187
|
# Works for python 3.1+
|
|
160
|
-
import imghdr
|
|
188
|
+
from metaflow._vendor import imghdr
|
|
161
189
|
|
|
162
190
|
resp = imghdr.what(None, h=data_object)
|
|
163
191
|
# Only accept types supported on the web
|
|
@@ -232,7 +260,8 @@ class TaskToDict:
|
|
|
232
260
|
supported_type = True
|
|
233
261
|
type_parsing_func = self._supported_types[obj_type_name]
|
|
234
262
|
data_obj = type_parsing_func(data_object)
|
|
235
|
-
if
|
|
263
|
+
# Secondary check: if the in-memory object size exceeds our limit, use repr instead
|
|
264
|
+
if _get_object_size(data_obj) > self._max_artifact_size:
|
|
236
265
|
data_obj = rep.repr(data_obj)
|
|
237
266
|
large_object = True
|
|
238
267
|
else:
|
|
@@ -314,8 +343,8 @@ class TaskToDict:
|
|
|
314
343
|
# If there is any form of TypeError or ValueError we set the column value to "Unsupported Type"
|
|
315
344
|
# We also set columns which are have null values to "null" strings
|
|
316
345
|
time_format = "%Y-%m-%dT%H:%M:%S%Z"
|
|
317
|
-
truncate_long_objects = (
|
|
318
|
-
|
|
346
|
+
truncate_long_objects = lambda x: (
|
|
347
|
+
x.astype("string").str.slice(0, 30) + "..."
|
|
319
348
|
if len(x) > 0 and x.astype("string").str.len().max() > 30
|
|
320
349
|
else x.astype("string")
|
|
321
350
|
)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JSONViewer component for displaying JSON data with syntax highlighting and collapsible sections.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional, Union
|
|
6
|
+
from .card import MetaflowCardComponent, with_default_component_id
|
|
7
|
+
from .renderer_tools import render_safely
|
|
8
|
+
import json
|
|
9
|
+
from metaflow._vendor import yaml
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JSONViewer(MetaflowCardComponent):
|
|
13
|
+
"""
|
|
14
|
+
A component for displaying JSON data with syntax highlighting and collapsible sections.
|
|
15
|
+
|
|
16
|
+
This component provides a rich view of JSON data with proper formatting, syntax highlighting,
|
|
17
|
+
and the ability to collapse/expand sections for better readability.
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
```python
|
|
21
|
+
from metaflow.cards import JSONViewer
|
|
22
|
+
from metaflow import current
|
|
23
|
+
|
|
24
|
+
data = {
|
|
25
|
+
"user": {"name": "Alice", "age": 30},
|
|
26
|
+
"items": [{"id": 1, "name": "Item 1"}, {"id": 2, "name": "Item 2"}],
|
|
27
|
+
"metadata": {"created": "2024-01-01", "version": "1.0"}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
json_viewer = JSONViewer(data, collapsible=True, max_height="400px")
|
|
31
|
+
current.card.append(json_viewer)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
data : Any
|
|
37
|
+
The data to display as JSON. Will be serialized using json.dumps().
|
|
38
|
+
collapsible : bool, default True
|
|
39
|
+
Whether to make the JSON viewer collapsible.
|
|
40
|
+
max_height : str, optional
|
|
41
|
+
Maximum height for the viewer (CSS value like "300px" or "20rem").
|
|
42
|
+
show_copy_button : bool, default True
|
|
43
|
+
Whether to show a copy-to-clipboard button.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
type = "jsonViewer"
|
|
47
|
+
|
|
48
|
+
REALTIME_UPDATABLE = True
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
data: Any,
|
|
53
|
+
collapsible: bool = True,
|
|
54
|
+
max_height: Optional[str] = None,
|
|
55
|
+
show_copy_button: bool = True,
|
|
56
|
+
title: Optional[str] = None,
|
|
57
|
+
):
|
|
58
|
+
self._data = data
|
|
59
|
+
self._collapsible = collapsible
|
|
60
|
+
self._max_height = max_height
|
|
61
|
+
self._show_copy_button = show_copy_button
|
|
62
|
+
self._title = title
|
|
63
|
+
|
|
64
|
+
def update(self, data: Any):
|
|
65
|
+
"""
|
|
66
|
+
Update the JSON data.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
data : Any
|
|
71
|
+
New data to display as JSON.
|
|
72
|
+
"""
|
|
73
|
+
self._data = data
|
|
74
|
+
|
|
75
|
+
@with_default_component_id
|
|
76
|
+
@render_safely
|
|
77
|
+
def render(self):
|
|
78
|
+
# Serialize data to JSON string
|
|
79
|
+
try:
|
|
80
|
+
if isinstance(self._data, str):
|
|
81
|
+
# If already a string, try to parse and re-serialize for formatting
|
|
82
|
+
try:
|
|
83
|
+
parsed = json.loads(self._data)
|
|
84
|
+
json_string = json.dumps(parsed, indent=2, ensure_ascii=False)
|
|
85
|
+
except json.JSONDecodeError:
|
|
86
|
+
# If not valid JSON, treat as plain string
|
|
87
|
+
json_string = json.dumps(self._data, indent=2, ensure_ascii=False)
|
|
88
|
+
else:
|
|
89
|
+
json_string = json.dumps(
|
|
90
|
+
self._data, indent=2, ensure_ascii=False, default=str
|
|
91
|
+
)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
# Fallback for non-serializable objects
|
|
94
|
+
json_string = json.dumps(
|
|
95
|
+
{"error": f"Could not serialize data: {str(e)}"}, indent=2
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
data = {
|
|
99
|
+
"type": self.type,
|
|
100
|
+
"id": self.component_id,
|
|
101
|
+
"json_string": json_string,
|
|
102
|
+
"collapsible": self._collapsible,
|
|
103
|
+
"show_copy_button": self._show_copy_button,
|
|
104
|
+
"title": self._title or "JSON",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if self._max_height:
|
|
108
|
+
data["max_height"] = self._max_height
|
|
109
|
+
|
|
110
|
+
return data
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class YAMLViewer(MetaflowCardComponent):
|
|
114
|
+
"""
|
|
115
|
+
A component for displaying YAML data with syntax highlighting and collapsible sections.
|
|
116
|
+
|
|
117
|
+
This component provides a rich view of YAML data with proper formatting and syntax highlighting.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
```python
|
|
121
|
+
from metaflow.cards import YAMLViewer
|
|
122
|
+
from metaflow import current
|
|
123
|
+
|
|
124
|
+
data = {
|
|
125
|
+
"database": {
|
|
126
|
+
"host": "localhost",
|
|
127
|
+
"port": 5432,
|
|
128
|
+
"credentials": {"username": "admin", "password": "secret"}
|
|
129
|
+
},
|
|
130
|
+
"features": ["auth", "logging", "monitoring"]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
yaml_viewer = YAMLViewer(data, collapsible=True)
|
|
134
|
+
current.card.append(yaml_viewer)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
data : Any
|
|
140
|
+
The data to display as YAML. Will be serialized to YAML format.
|
|
141
|
+
collapsible : bool, default True
|
|
142
|
+
Whether to make the YAML viewer collapsible.
|
|
143
|
+
max_height : str, optional
|
|
144
|
+
Maximum height for the viewer (CSS value like "300px" or "20rem").
|
|
145
|
+
show_copy_button : bool, default True
|
|
146
|
+
Whether to show a copy-to-clipboard button.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
type = "yamlViewer"
|
|
150
|
+
|
|
151
|
+
REALTIME_UPDATABLE = True
|
|
152
|
+
|
|
153
|
+
def __init__(
|
|
154
|
+
self,
|
|
155
|
+
data: Any,
|
|
156
|
+
collapsible: bool = True,
|
|
157
|
+
max_height: Optional[str] = None,
|
|
158
|
+
show_copy_button: bool = True,
|
|
159
|
+
title: Optional[str] = None,
|
|
160
|
+
):
|
|
161
|
+
self._data = data
|
|
162
|
+
self._collapsible = collapsible
|
|
163
|
+
self._max_height = max_height
|
|
164
|
+
self._show_copy_button = show_copy_button
|
|
165
|
+
self._title = title
|
|
166
|
+
|
|
167
|
+
def update(self, data: Any):
|
|
168
|
+
"""
|
|
169
|
+
Update the YAML data.
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
data : Any
|
|
174
|
+
New data to display as YAML.
|
|
175
|
+
"""
|
|
176
|
+
self._data = data
|
|
177
|
+
|
|
178
|
+
def _to_yaml_string(self, data: Any) -> str:
|
|
179
|
+
"""
|
|
180
|
+
Convert data to YAML string format using vendored YAML module.
|
|
181
|
+
"""
|
|
182
|
+
try:
|
|
183
|
+
if isinstance(data, str):
|
|
184
|
+
# Try to parse as JSON first, then convert to YAML
|
|
185
|
+
try:
|
|
186
|
+
import json
|
|
187
|
+
|
|
188
|
+
parsed = json.loads(data)
|
|
189
|
+
yaml_result = yaml.dump(
|
|
190
|
+
parsed, default_flow_style=False, indent=2, sort_keys=False
|
|
191
|
+
)
|
|
192
|
+
return (
|
|
193
|
+
str(yaml_result)
|
|
194
|
+
if yaml_result is not None
|
|
195
|
+
else "# Empty YAML result"
|
|
196
|
+
)
|
|
197
|
+
except json.JSONDecodeError:
|
|
198
|
+
# If not JSON, return as-is
|
|
199
|
+
return data
|
|
200
|
+
else:
|
|
201
|
+
yaml_result = yaml.dump(
|
|
202
|
+
data, default_flow_style=False, indent=2, sort_keys=False
|
|
203
|
+
)
|
|
204
|
+
return (
|
|
205
|
+
str(yaml_result)
|
|
206
|
+
if yaml_result is not None
|
|
207
|
+
else "# Empty YAML result"
|
|
208
|
+
)
|
|
209
|
+
except Exception as e:
|
|
210
|
+
# Fallback to JSON on any error
|
|
211
|
+
import json
|
|
212
|
+
|
|
213
|
+
return f"# Error converting to YAML: {str(e)}\n{json.dumps(data, indent=2, default=str)}"
|
|
214
|
+
|
|
215
|
+
@with_default_component_id
|
|
216
|
+
@render_safely
|
|
217
|
+
def render(self):
|
|
218
|
+
yaml_string = self._to_yaml_string(self._data)
|
|
219
|
+
|
|
220
|
+
data = {
|
|
221
|
+
"type": self.type,
|
|
222
|
+
"id": self.component_id,
|
|
223
|
+
"yaml_string": yaml_string,
|
|
224
|
+
"collapsible": self._collapsible,
|
|
225
|
+
"show_copy_button": self._show_copy_button,
|
|
226
|
+
"title": self._title or "YAML",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if self._max_height:
|
|
230
|
+
data["max_height"] = self._max_height
|
|
231
|
+
|
|
232
|
+
return data
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap";code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:#ffffff80}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}:root{--bg: #ffffff;--black: #333;--blue: #0c66de;--dk-grey: #767676;--dk-primary: #ef863b;--dk-secondary: #13172d;--dk-tertiary: #0f426e;--error: #cf483e;--grey: rgba(0, 0, 0, .125);--highlight: #f8d9d8;--lt-blue: #4fa7ff;--lt-grey: #f3f3f3;--lt-lt-grey: #f9f9f9;--lt-primary: #ffcb8b;--lt-secondary: #434d81;--lt-tertiary: #4189c9;--primary: #faab4a;--quadrary: #f8d9d8;--secondary: #2e3454;--tertiary: #2a679d;--white: #ffffff;--component-spacer: 3rem;--aside-width: 20rem;--embed-card-min-height: 12rem;--mono-font: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace}html,body{margin:0;min-height:100vh;overflow-y:visible;padding:0;width:100%}.card_app{width:100%;min-height:100vh}.embed .card_app{min-height:var(--embed-card-min-height)}.mf-card *{box-sizing:border-box}.mf-card{background:var(--bg);color:var(--black);font-family:Roboto,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;font-size:14px;font-weight:400;line-height:1.5;text-size-adjust:100%;margin:0;min-height:100vh;overflow-y:visible;padding:0;text-align:left;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;width:100%}.embed .mf-card{min-height:var(--embed-card-min-height)}.mf-card :is(.mono,code.mono,pre.mono){font-family:var(--mono-font);font-weight:lighter}.mf-card :is(table,th,td){border-spacing:1px;text-align:center;color:var(--black)}.mf-card table{position:relative;min-width:100%;table-layout:inherit!important}.mf-card td{padding:.66rem 1.25rem;background:var(--lt-lt-grey);border:none}.mf-card th{border:none;color:var(--dk-grey);font-weight:400;padding:.5rem}.mf-card :is(h1,h2,h3,h4,h5){font-weight:700;margin:.5rem 0}.mf-card ul{margin:0;padding:0}.mf-card p{margin:0 0 1rem}.mf-card p:last-of-type{margin:0}.mf-card button{font-size:1rem}.mf-card .textButton{cursor:pointer;text-align:left;background:none;border:1px solid transparent;outline:none;padding:0}.mf-card :is(button.textButton:focus,a:focus,button.textButton:active){border:1px dashed var(--grey);background:transparent}.mf-card button.textButton:hover{color:var(--blue);text-decoration:none}.mf-card :is(:not(pre)>code[class*=language-],pre[class*=language-]){background:transparent!important;text-shadow:none;-webkit-user-select:auto;user-select:auto}aside.svelte-1okdv0e{display:none;line-height:2;text-align:left}@media (min-width: 60rem){aside.svelte-1okdv0e{display:flex;flex-direction:column;height:100vh;justify-content:space-between;padding:2.5rem 0 1.5rem 1.5rem;position:fixed;width:var(--aside-width)}}.embed aside{display:none}aside ul{list-style-type:none}aside a,aside button,aside a:visited{text-decoration:none;cursor:pointer;font-weight:700;color:var(--black)}aside a:hover,aside button:hover{text-decoration:underline}.logoContainer svg{width:100%;max-width:140px;margin-bottom:3.75rem;height:auto}.idCell.svelte-pt8vzv{font-weight:700;text-align:right;background:var(--lt-grey);width:12%}.codeCell.svelte-pt8vzv{text-align:left;-webkit-user-select:all;user-select:all}.container.svelte-ubs992{width:100%;overflow:auto}table.svelte-ubs992{width:100%}:root{--dag-border: #282828;--dag-bg-static: var(--lt-grey);--dag-bg-success: #a5d46a;--dag-bg-running: #ffdf80;--dag-bg-error: #ffa080;--dag-connector: #cccccc;--dag-gap: 5rem;--dag-step-height: 6.25rem;--dag-step-width: 11.25rem;--dag-selected: #ffd700}.connectorwrapper.svelte-1hyaq5f{transform-origin:0 0;position:absolute;z-index:0;min-width:var(--strokeWidth)}.flip.svelte-1hyaq5f{transform:scaleX(-1)}.path.svelte-1hyaq5f{--strokeWidth: .5rem;--strokeColor: var(--dag-connector);--borderRadius: 1.25rem;box-sizing:border-box}.straightLine.svelte-1hyaq5f{position:absolute;inset:0;border-left:var(--strokeWidth) solid var(--strokeColor)}.topLeft.svelte-1hyaq5f{position:absolute;top:0;left:0;right:50%;bottom:calc(var(--dag-gap) / 2 - var(--strokeWidth) / 2);border-radius:0 0 0 var(--borderRadius);border-left:var(--strokeWidth) solid var(--strokeColor);border-bottom:var(--strokeWidth) solid var(--strokeColor)}.bottomRight.svelte-1hyaq5f{position:absolute;top:calc(100% - (var(--dag-gap) / 2 + var(--strokeWidth) / 2));left:50%;right:0;bottom:0;border-radius:0 var(--borderRadius) 0 0;border-top:var(--strokeWidth) solid var(--strokeColor);border-right:var(--strokeWidth) solid var(--strokeColor)}.wrapper.svelte-117ceti{position:relative;z-index:1}.step.svelte-117ceti{font-size:.75rem;padding:.5rem;color:var(--dk-grey)}.rectangle.svelte-117ceti{background-color:var(--dag-bg-static);border:1px solid var(--dag-border);box-sizing:border-box;position:relative;height:var(--dag-step-height);width:var(--dag-step-width)}.rectangle.error.svelte-117ceti{background-color:var(--dag-bg-error)}.rectangle.success.svelte-117ceti{background-color:var(--dag-bg-success)}.rectangle.running.svelte-117ceti{background-color:var(--dag-bg-running)}.level.svelte-117ceti{z-index:-1;filter:contrast(.5);position:absolute}.inner.svelte-117ceti{position:relative;height:100%;width:100%}.name.svelte-117ceti{font-weight:700;overflow:hidden;text-overflow:ellipsis;display:block}.description.svelte-117ceti{position:absolute;max-height:4rem;bottom:0;left:0;right:0;overflow:hidden;-webkit-line-clamp:4;line-clamp:4;display:-webkit-box;-webkit-box-orient:vertical}.overflown.description.svelte-117ceti{cursor:help}.current.svelte-117ceti .rectangle:where(.svelte-117ceti){box-shadow:0 0 10px var(--dag-selected)}.levelstoshow.svelte-117ceti{position:absolute;bottom:100%;right:0;font-size:.75rem;font-weight:100;text-align:right}.stepwrapper.svelte-18aex7a{display:flex;align-items:center;flex-direction:column;width:100%;position:relative;min-width:var(--dag-step-width)}.childwrapper.svelte-18aex7a{display:flex;width:100%}.gap.svelte-18aex7a{height:var(--dag-gap)}.title.svelte-117s0ws{text-align:left}.subtitle.svelte-lu9pnn{font-size:1rem;text-align:left}header.svelte-1ugmt5d{margin-bottom:var(--component-spacer)}figure.svelte-1x96yvr{background:var(--lt-grey);padding:1rem;border-radius:5px;text-align:center;margin:0 auto var(--component-spacer)}@media (min-width: 60rem){figure.svelte-1x96yvr{margin-bottom:0}}img.svelte-1x96yvr{max-width:100%;max-height:500px}.label.svelte-1x96yvr{font-weight:700;margin:.5rem 0}.description.svelte-1x96yvr{font-size:.9rem;font-style:italic;text-align:center;margin:.5rem 0}.log.svelte-1jhmsu{background:var(--lt-grey)!important;font-size:.9rem;padding:2rem}.page.svelte-v7ihqd:last-of-type{margin-bottom:var(--component-spacer)}.page:last-of-type section:last-of-type hr{display:none}progress.svelte-ljrmzp::-webkit-progress-bar{background-color:#fff!important;min-width:100%}progress.svelte-ljrmzp{background-color:#fff;color:#326cded9!important}progress.svelte-ljrmzp::-moz-progress-bar{background-color:#326cde!important}table .container{background:transparent!important;font-size:10px!important;padding:0!important}table progress{height:4px!important}.container.svelte-ljrmzp{display:flex;align-items:center;justify-content:center;font-size:12px;border-radius:3px;background:#edf5ff;padding:3rem}.inner.svelte-ljrmzp{max-width:410px;width:100%;text-align:center}.info.svelte-ljrmzp{display:flex;justify-content:space-between}table .info{text-align:left;flex-direction:column}label.svelte-ljrmzp{font-weight:700}.labelValue.svelte-ljrmzp{border-left:1px solid rgba(0,0,0,.1);margin-left:.25rem;padding-left:.5rem}.details.svelte-ljrmzp{font-family:var(--mono-font);font-size:8px;color:#333433;line-height:18px;overflow:hidden;white-space:nowrap}progress.svelte-ljrmzp{width:100%;border:none;border-radius:5px;height:8px;background:#fff}.heading.svelte-17n0qr8{margin-bottom:1.5rem}.sectionItems.svelte-17n0qr8{display:block}.sectionItems .imageContainer{max-height:500px}.container.svelte-17n0qr8{scroll-margin:var(--component-spacer)}hr.svelte-17n0qr8{background:var(--grey);border:none;height:1px;margin:var(--component-spacer) 0;padding:0}@media (min-width: 60rem){.sectionItems.svelte-17n0qr8{display:grid;grid-gap:2rem}}td.svelte-gl9h79{text-align:left}td.labelColumn.svelte-gl9h79{text-align:right;background-color:var(--lt-grey);font-weight:700;width:12%;white-space:nowrap}.tableContainer.svelte-q3hq57{overflow:auto}th.svelte-q3hq57{position:sticky;top:-1px;z-index:2;white-space:nowrap;background:var(--white)}.mainContainer.svelte-mqeomk{max-width:110rem}main.svelte-mqeomk{flex:0 1 auto;max-width:100rem;padding:1.5rem}@media (min-width: 60rem){main.svelte-mqeomk{margin-left:var(--aside-width)}}.embed main{margin:0 auto;min-width:80%}.modal.svelte-1hhf5ym{align-items:center;background:#00000080;cursor:pointer;display:flex;height:100%;justify-content:center;inset:0;overflow:hidden;position:fixed;width:100%;z-index:100}.modalContainer>*{background-color:#fff;border-radius:5px;cursor:default;flex:0 1 auto;padding:1rem;position:relative}.modal img{max-height:80vh!important}.cancelButton.svelte-1hhf5ym{color:#fff;cursor:pointer;font-size:2rem;position:absolute;right:1rem;top:1rem}.cancelButton.svelte-1hhf5ym:hover{color:var(--blue)}.nav.svelte-1kdpgko{border-radius:0 0 5px;display:none;margin:0;top:0}ul.navList.svelte-1kdpgko{list-style-type:none}ul.navList.svelte-1kdpgko ul:where(.svelte-1kdpgko){margin:.5rem 1rem 2rem}.navList.svelte-1kdpgko li:where(.svelte-1kdpgko){display:block;margin:0}.navItem.svelte-1kdpgko li:where(.svelte-1kdpgko):hover{color:var(--blue)}.pageId.svelte-1kdpgko{display:block;border-bottom:1px solid var(--grey);padding:0 .5rem;margin-bottom:1rem}@media (min-width: 60rem){.nav.svelte-1kdpgko{display:block}ul.navList.svelte-1kdpgko{text-align:left}.navList.svelte-1kdpgko li:where(.svelte-1kdpgko){display:block;margin:.5rem 0}}.container.svelte-teyund{width:100%;display:flex;flex-direction:column;position:relative}
|