reflex 0.4.5a1__py3-none-any.whl → 0.4.6__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.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +5 -15
- reflex/.templates/jinja/web/pages/index.js.jinja2 +4 -0
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
- reflex/.templates/web/utils/state.js +3 -0
- reflex/__init__.py +12 -2
- reflex/__init__.pyi +4 -0
- reflex/app.py +23 -3
- reflex/base.py +16 -4
- reflex/compiler/compiler.py +1 -0
- reflex/compiler/utils.py +11 -2
- reflex/components/base/app_wrap.pyi +1 -1
- reflex/components/base/bare.py +3 -4
- reflex/components/base/body.pyi +1 -1
- reflex/components/base/document.pyi +1 -1
- reflex/components/base/fragment.pyi +1 -1
- reflex/components/base/head.pyi +1 -1
- reflex/components/base/link.pyi +1 -1
- reflex/components/base/meta.pyi +1 -1
- reflex/components/base/script.pyi +1 -1
- reflex/components/chakra/base.pyi +1 -1
- reflex/components/chakra/datadisplay/badge.pyi +1 -1
- reflex/components/chakra/datadisplay/code.pyi +1 -1
- reflex/components/chakra/datadisplay/divider.pyi +1 -1
- reflex/components/chakra/datadisplay/keyboard_key.pyi +1 -1
- reflex/components/chakra/datadisplay/list.pyi +1 -1
- reflex/components/chakra/datadisplay/stat.pyi +1 -1
- reflex/components/chakra/datadisplay/table.pyi +1 -1
- reflex/components/chakra/datadisplay/tag.pyi +1 -1
- reflex/components/chakra/disclosure/accordion.pyi +1 -1
- reflex/components/chakra/disclosure/tabs.pyi +1 -1
- reflex/components/chakra/disclosure/transition.pyi +1 -1
- reflex/components/chakra/disclosure/visuallyhidden.pyi +1 -1
- reflex/components/chakra/feedback/alert.pyi +1 -1
- reflex/components/chakra/feedback/circularprogress.pyi +1 -1
- reflex/components/chakra/feedback/progress.pyi +1 -1
- reflex/components/chakra/feedback/skeleton.pyi +1 -1
- reflex/components/chakra/feedback/spinner.pyi +1 -1
- reflex/components/chakra/forms/button.pyi +1 -1
- reflex/components/chakra/forms/checkbox.pyi +1 -1
- reflex/components/chakra/forms/colormodeswitch.pyi +1 -1
- reflex/components/chakra/forms/date_picker.pyi +1 -1
- reflex/components/chakra/forms/date_time_picker.pyi +1 -1
- reflex/components/chakra/forms/editable.pyi +1 -1
- reflex/components/chakra/forms/email.pyi +1 -1
- reflex/components/chakra/forms/form.pyi +1 -1
- reflex/components/chakra/forms/iconbutton.pyi +1 -1
- reflex/components/chakra/forms/input.pyi +1 -1
- reflex/components/chakra/forms/numberinput.pyi +1 -1
- reflex/components/chakra/forms/password.pyi +1 -1
- reflex/components/chakra/forms/pininput.pyi +1 -1
- reflex/components/chakra/forms/radio.pyi +1 -1
- reflex/components/chakra/forms/rangeslider.pyi +1 -1
- reflex/components/chakra/forms/select.pyi +1 -1
- reflex/components/chakra/forms/slider.pyi +1 -1
- reflex/components/chakra/forms/switch.pyi +1 -1
- reflex/components/chakra/forms/textarea.pyi +1 -1
- reflex/components/chakra/forms/time_picker.pyi +1 -1
- reflex/components/chakra/layout/aspect_ratio.pyi +1 -1
- reflex/components/chakra/layout/box.pyi +1 -1
- reflex/components/chakra/layout/card.pyi +1 -1
- reflex/components/chakra/layout/center.pyi +1 -1
- reflex/components/chakra/layout/container.pyi +1 -1
- reflex/components/chakra/layout/flex.pyi +1 -1
- reflex/components/chakra/layout/grid.pyi +1 -1
- reflex/components/chakra/layout/spacer.pyi +1 -1
- reflex/components/chakra/layout/stack.pyi +1 -1
- reflex/components/chakra/layout/wrap.pyi +1 -1
- reflex/components/chakra/media/avatar.pyi +1 -1
- reflex/components/chakra/media/icon.pyi +1 -1
- reflex/components/chakra/media/image.pyi +1 -1
- reflex/components/chakra/navigation/breadcrumb.pyi +1 -1
- reflex/components/chakra/navigation/link.pyi +1 -1
- reflex/components/chakra/navigation/linkoverlay.pyi +1 -1
- reflex/components/chakra/navigation/stepper.pyi +1 -1
- reflex/components/chakra/overlay/alertdialog.pyi +1 -1
- reflex/components/chakra/overlay/drawer.pyi +1 -1
- reflex/components/chakra/overlay/menu.pyi +1 -1
- reflex/components/chakra/overlay/modal.pyi +1 -1
- reflex/components/chakra/overlay/popover.pyi +1 -1
- reflex/components/chakra/overlay/tooltip.pyi +1 -1
- reflex/components/chakra/typography/heading.pyi +1 -1
- reflex/components/chakra/typography/highlight.pyi +1 -1
- reflex/components/chakra/typography/span.pyi +1 -1
- reflex/components/chakra/typography/text.pyi +1 -1
- reflex/components/component.py +82 -30
- reflex/components/core/banner.py +1 -2
- reflex/components/core/banner.pyi +1 -2
- reflex/components/core/client_side_routing.pyi +1 -1
- reflex/components/core/cond.py +1 -1
- reflex/components/core/debounce.pyi +1 -1
- reflex/components/core/html.pyi +1 -1
- reflex/components/core/responsive.py +1 -1
- reflex/components/core/upload.pyi +1 -1
- reflex/components/datadisplay/code.py +17 -9
- reflex/components/datadisplay/code.pyi +3 -1
- reflex/components/datadisplay/dataeditor.pyi +1 -1
- reflex/components/el/element.pyi +1 -1
- reflex/components/el/elements/base.pyi +1 -1
- reflex/components/el/elements/forms.py +78 -1
- reflex/components/el/elements/forms.pyi +10 -2
- reflex/components/el/elements/inline.pyi +1 -1
- reflex/components/el/elements/media.pyi +1 -1
- reflex/components/el/elements/metadata.pyi +1 -1
- reflex/components/el/elements/other.pyi +1 -1
- reflex/components/el/elements/scripts.pyi +1 -1
- reflex/components/el/elements/sectioning.pyi +1 -1
- reflex/components/el/elements/tables.pyi +1 -1
- reflex/components/el/elements/typography.pyi +1 -1
- reflex/components/gridjs/datatable.pyi +1 -1
- reflex/components/lucide/icon.py +275 -115
- reflex/components/lucide/icon.pyi +259 -114
- reflex/components/markdown/markdown.pyi +1 -1
- reflex/components/moment/moment.pyi +1 -1
- reflex/components/next/base.pyi +1 -1
- reflex/components/next/image.pyi +1 -1
- reflex/components/next/link.pyi +1 -1
- reflex/components/next/video.pyi +1 -1
- reflex/components/plotly/plotly.pyi +1 -1
- reflex/components/radix/primitives/accordion.pyi +1 -1
- reflex/components/radix/primitives/base.pyi +1 -1
- reflex/components/radix/primitives/drawer.pyi +1 -1
- reflex/components/radix/primitives/form.pyi +1 -1
- reflex/components/radix/primitives/progress.pyi +1 -1
- reflex/components/radix/primitives/slider.pyi +1 -1
- reflex/components/radix/themes/base.pyi +1 -1
- reflex/components/radix/themes/color_mode.pyi +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
- reflex/components/radix/themes/components/aspect_ratio.pyi +1 -1
- reflex/components/radix/themes/components/avatar.pyi +1 -1
- reflex/components/radix/themes/components/badge.pyi +1 -1
- reflex/components/radix/themes/components/button.pyi +1 -1
- reflex/components/radix/themes/components/callout.pyi +1 -1
- reflex/components/radix/themes/components/card.pyi +1 -1
- reflex/components/radix/themes/components/checkbox.pyi +1 -1
- reflex/components/radix/themes/components/context_menu.pyi +1 -1
- reflex/components/radix/themes/components/dialog.pyi +1 -1
- reflex/components/radix/themes/components/dropdown_menu.pyi +1 -1
- reflex/components/radix/themes/components/hover_card.pyi +1 -1
- reflex/components/radix/themes/components/icon_button.pyi +1 -1
- reflex/components/radix/themes/components/inset.pyi +1 -1
- reflex/components/radix/themes/components/popover.pyi +1 -1
- reflex/components/radix/themes/components/radio_group.pyi +1 -1
- reflex/components/radix/themes/components/scroll_area.pyi +1 -1
- reflex/components/radix/themes/components/select.py +4 -1
- reflex/components/radix/themes/components/select.pyi +5 -1
- reflex/components/radix/themes/components/separator.pyi +1 -1
- reflex/components/radix/themes/components/slider.pyi +1 -1
- reflex/components/radix/themes/components/switch.pyi +1 -1
- reflex/components/radix/themes/components/table.pyi +1 -1
- reflex/components/radix/themes/components/tabs.pyi +1 -1
- reflex/components/radix/themes/components/text_area.pyi +5 -1
- reflex/components/radix/themes/components/text_field.pyi +1 -1
- reflex/components/radix/themes/components/tooltip.pyi +1 -1
- reflex/components/radix/themes/layout/__init__.py +5 -4
- reflex/components/radix/themes/layout/base.pyi +1 -1
- reflex/components/radix/themes/layout/box.pyi +1 -1
- reflex/components/radix/themes/layout/center.pyi +1 -1
- reflex/components/radix/themes/layout/container.pyi +1 -1
- reflex/components/radix/themes/layout/flex.pyi +1 -1
- reflex/components/radix/themes/layout/grid.pyi +1 -1
- reflex/components/radix/themes/layout/list.py +21 -13
- reflex/components/radix/themes/layout/list.pyi +139 -481
- reflex/components/radix/themes/layout/section.pyi +1 -1
- reflex/components/radix/themes/layout/spacer.pyi +1 -1
- reflex/components/radix/themes/layout/stack.pyi +1 -1
- reflex/components/radix/themes/typography/blockquote.pyi +1 -1
- reflex/components/radix/themes/typography/code.pyi +1 -1
- reflex/components/radix/themes/typography/heading.pyi +1 -1
- reflex/components/radix/themes/typography/link.pyi +1 -1
- reflex/components/radix/themes/typography/text.pyi +1 -1
- reflex/components/react_player/audio.pyi +1 -1
- reflex/components/react_player/react_player.pyi +1 -1
- reflex/components/react_player/video.pyi +1 -1
- reflex/components/recharts/cartesian.pyi +1 -1
- reflex/components/recharts/charts.pyi +1 -1
- reflex/components/recharts/general.pyi +1 -1
- reflex/components/recharts/polar.pyi +1 -1
- reflex/components/recharts/recharts.pyi +1 -1
- reflex/components/suneditor/editor.pyi +1 -1
- reflex/config.py +12 -2
- reflex/custom_components/custom_components.py +305 -21
- reflex/event.py +36 -46
- reflex/reflex.py +19 -13
- reflex/state.py +184 -39
- reflex/testing.py +15 -11
- reflex/utils/console.py +15 -7
- reflex/utils/exec.py +9 -0
- reflex/utils/prerequisites.py +12 -1
- reflex/utils/processes.py +8 -25
- reflex/utils/pyi_generator.py +842 -0
- reflex/utils/telemetry.py +18 -1
- reflex/utils/types.py +14 -2
- reflex/vars.py +33 -3
- {reflex-0.4.5a1.dist-info → reflex-0.4.6.dist-info}/METADATA +31 -29
- {reflex-0.4.5a1.dist-info → reflex-0.4.6.dist-info}/RECORD +198 -198
- reflex/page.pyi +0 -17
- {reflex-0.4.5a1.dist-info → reflex-0.4.6.dist-info}/LICENSE +0 -0
- {reflex-0.4.5a1.dist-info → reflex-0.4.6.dist-info}/WHEEL +0 -0
- {reflex-0.4.5a1.dist-info → reflex-0.4.6.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/layout/section.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/layout/spacer.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/layout/stack.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/typography/blockquote.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/typography/code.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/typography/heading.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/typography/link.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/radix/themes/typography/text.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/react_player/audio.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/react_player/react_player.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/react_player/video.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/recharts/cartesian.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/recharts/charts.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/recharts/general.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/recharts/polar.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/recharts/recharts.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Stub file for reflex/components/suneditor/editor.py"""
|
|
2
2
|
# ------------------- DO NOT EDIT ----------------------
|
|
3
|
-
# This file was generated by `
|
|
3
|
+
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
4
4
|
# ------------------------------------------------------
|
|
5
5
|
|
|
6
6
|
from typing import Any, Dict, Literal, Optional, Union, overload
|
reflex/config.py
CHANGED
|
@@ -6,9 +6,19 @@ import importlib
|
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
8
|
import urllib.parse
|
|
9
|
-
from typing import Any, Dict, List, Optional, Set
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
# TODO The type checking guard can be removed once
|
|
13
|
+
# reflex-hosting-cli tools are compatible with pydantic v2
|
|
14
|
+
|
|
15
|
+
if not TYPE_CHECKING:
|
|
16
|
+
import pydantic.v1 as pydantic
|
|
17
|
+
else:
|
|
18
|
+
raise ModuleNotFoundError
|
|
19
|
+
except ModuleNotFoundError:
|
|
20
|
+
import pydantic
|
|
10
21
|
|
|
11
|
-
import pydantic
|
|
12
22
|
from reflex_cli.constants.hosting import Hosting
|
|
13
23
|
|
|
14
24
|
from reflex import constants
|
|
@@ -9,18 +9,32 @@ import sys
|
|
|
9
9
|
from collections import namedtuple
|
|
10
10
|
from contextlib import contextmanager
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Optional
|
|
12
|
+
from typing import Optional, Tuple
|
|
13
13
|
|
|
14
|
+
import httpx
|
|
15
|
+
import tomlkit
|
|
14
16
|
import typer
|
|
17
|
+
from tomlkit.exceptions import TOMLKitError
|
|
15
18
|
|
|
16
19
|
from reflex import constants
|
|
17
20
|
from reflex.config import get_config
|
|
18
21
|
from reflex.constants import CustomComponents
|
|
19
22
|
from reflex.utils import console
|
|
23
|
+
from reflex.utils.pyi_generator import PyiGenerator
|
|
20
24
|
|
|
21
25
|
config = get_config()
|
|
22
26
|
custom_components_cli = typer.Typer()
|
|
23
27
|
|
|
28
|
+
POST_CUSTOM_COMPONENTS_GALLERY_ENDPOINT = (
|
|
29
|
+
f"{config.cp_backend_url}/custom-components/gallery"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
GET_CUSTOM_COMPONENTS_GALLERY_BY_NAME_ENDPOINT = (
|
|
33
|
+
f"{config.cp_backend_url}/custom-components/gallery"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
POST_CUSTOM_COMPONENTS_GALLERY_TIMEOUT = 15
|
|
37
|
+
|
|
24
38
|
|
|
25
39
|
@contextmanager
|
|
26
40
|
def set_directory(working_directory: str):
|
|
@@ -400,6 +414,18 @@ def _run_commands_in_subprocess(cmds: list[str]) -> bool:
|
|
|
400
414
|
return False
|
|
401
415
|
|
|
402
416
|
|
|
417
|
+
def _make_pyi_files():
|
|
418
|
+
"""Create pyi files for the custom component."""
|
|
419
|
+
from glob import glob
|
|
420
|
+
|
|
421
|
+
package_name = glob("custom_components/*.egg-info")[0].replace(".egg-info", "")
|
|
422
|
+
|
|
423
|
+
for dir, _, _ in os.walk(f"./{package_name}"):
|
|
424
|
+
if "__pycache__" in dir:
|
|
425
|
+
continue
|
|
426
|
+
PyiGenerator().scan_all([dir])
|
|
427
|
+
|
|
428
|
+
|
|
403
429
|
def _run_build():
|
|
404
430
|
"""Run the build command.
|
|
405
431
|
|
|
@@ -408,6 +434,8 @@ def _run_build():
|
|
|
408
434
|
"""
|
|
409
435
|
console.print("Building custom component...")
|
|
410
436
|
|
|
437
|
+
_make_pyi_files()
|
|
438
|
+
|
|
411
439
|
cmds = [sys.executable, "-m", "build", "."]
|
|
412
440
|
if _run_commands_in_subprocess(cmds):
|
|
413
441
|
console.info("Custom component built successfully!")
|
|
@@ -493,21 +521,15 @@ def _get_version_to_publish() -> str:
|
|
|
493
521
|
The version to publish.
|
|
494
522
|
"""
|
|
495
523
|
# Get the version from the pyproject.toml.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if match:
|
|
502
|
-
version_to_publish = match.group(1)
|
|
503
|
-
console.debug(f"Version to be published: {version_to_publish}")
|
|
504
|
-
if not version_to_publish:
|
|
524
|
+
try:
|
|
525
|
+
with open(CustomComponents.PYPROJECT_TOML, "rb") as f:
|
|
526
|
+
project_toml = tomlkit.parse(f.read())
|
|
527
|
+
return project_toml.get("project", {})["version"]
|
|
528
|
+
except (OSError, KeyError, TOMLKitError) as ex:
|
|
505
529
|
console.error(
|
|
506
|
-
f"
|
|
530
|
+
f"Cannot find the version in {CustomComponents.PYPROJECT_TOML} due to {ex}"
|
|
507
531
|
)
|
|
508
|
-
raise typer.Exit(code=1)
|
|
509
|
-
|
|
510
|
-
return version_to_publish
|
|
532
|
+
raise typer.Exit(code=1) from ex
|
|
511
533
|
|
|
512
534
|
|
|
513
535
|
def _ensure_dist_dir(version_to_publish: str, build: bool):
|
|
@@ -542,7 +564,7 @@ def _ensure_dist_dir(version_to_publish: str, build: bool):
|
|
|
542
564
|
needs_rebuild = (
|
|
543
565
|
console.ask(
|
|
544
566
|
"Distribution files for the version to be published already exist. Do you want to rebuild?",
|
|
545
|
-
choices=["
|
|
567
|
+
choices=["y", "n"],
|
|
546
568
|
default="n",
|
|
547
569
|
)
|
|
548
570
|
== "y"
|
|
@@ -601,6 +623,14 @@ def publish(
|
|
|
601
623
|
True,
|
|
602
624
|
help="Whether to build the package before publishing. If the package is already built, set this to False.",
|
|
603
625
|
),
|
|
626
|
+
share: bool = typer.Option(
|
|
627
|
+
True,
|
|
628
|
+
help="Whether to prompt to share more details on the published package. Only applicable when published to PyPI. Defaults to True.",
|
|
629
|
+
),
|
|
630
|
+
validate_project_info: bool = typer.Option(
|
|
631
|
+
True,
|
|
632
|
+
help="Whether to interactively validate the project information in the pyproject.toml file.",
|
|
633
|
+
),
|
|
604
634
|
loglevel: constants.LogLevel = typer.Option(
|
|
605
635
|
config.loglevel, help="The log level to use."
|
|
606
636
|
),
|
|
@@ -613,6 +643,8 @@ def publish(
|
|
|
613
643
|
username: The username to use for authentication on python package repository.
|
|
614
644
|
password: The password to use for authentication on python package repository.
|
|
615
645
|
build: Whether to build the distribution files. Defaults to True.
|
|
646
|
+
share: Whether to prompt to share more details on the published package. Defaults to True.
|
|
647
|
+
validate_project_info: whether to interactively validate the project information in the pyproject.toml file. Defaults to True.
|
|
616
648
|
loglevel: The log level to use.
|
|
617
649
|
|
|
618
650
|
Raises:
|
|
@@ -633,12 +665,16 @@ def publish(
|
|
|
633
665
|
# Validate the distribution directory.
|
|
634
666
|
_ensure_dist_dir(version_to_publish=version_to_publish, build=build)
|
|
635
667
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
668
|
+
if validate_project_info and (
|
|
669
|
+
console.ask(
|
|
670
|
+
"Would you like to interactively review the package information?",
|
|
671
|
+
choices=["y", "n"],
|
|
672
|
+
default="y",
|
|
673
|
+
)
|
|
674
|
+
== "y"
|
|
675
|
+
):
|
|
676
|
+
_validate_project_info()
|
|
677
|
+
|
|
642
678
|
publish_cmds = [
|
|
643
679
|
sys.executable,
|
|
644
680
|
"-m",
|
|
@@ -657,3 +693,251 @@ def publish(
|
|
|
657
693
|
console.info("Custom component published successfully!")
|
|
658
694
|
else:
|
|
659
695
|
raise typer.Exit(1)
|
|
696
|
+
|
|
697
|
+
# Only prompt to share more details on the published package if it is published to PyPI.
|
|
698
|
+
if repository != "pypi" or not share:
|
|
699
|
+
return
|
|
700
|
+
|
|
701
|
+
# Ask user to share more details on the published package.
|
|
702
|
+
if (
|
|
703
|
+
console.ask(
|
|
704
|
+
"Would you like to include your published component on our gallery?",
|
|
705
|
+
choices=["y", "n"],
|
|
706
|
+
default="y",
|
|
707
|
+
)
|
|
708
|
+
== "n"
|
|
709
|
+
):
|
|
710
|
+
console.print(
|
|
711
|
+
"If you decide to do this later, you can run `reflex component share` command. Thank you!"
|
|
712
|
+
)
|
|
713
|
+
return
|
|
714
|
+
|
|
715
|
+
_collect_details_for_gallery()
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def _process_entered_list(input: str) -> list | None:
|
|
719
|
+
"""Process the user entered comma separated list into a list if applicable.
|
|
720
|
+
|
|
721
|
+
Args:
|
|
722
|
+
input: the user entered comma separated list
|
|
723
|
+
|
|
724
|
+
Returns:
|
|
725
|
+
The list of items or None.
|
|
726
|
+
"""
|
|
727
|
+
return [t.strip() for t in input.split(",") if t if input] or None
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def _validate_project_info():
|
|
731
|
+
"""Validate the project information in the pyproject.toml file.
|
|
732
|
+
|
|
733
|
+
Raises:
|
|
734
|
+
Exit: If the pyproject.toml file is ill-formed.
|
|
735
|
+
"""
|
|
736
|
+
try:
|
|
737
|
+
with open(CustomComponents.PYPROJECT_TOML, "rb") as f:
|
|
738
|
+
pyproject_toml = tomlkit.parse(f.read())
|
|
739
|
+
except TOMLKitError as ex:
|
|
740
|
+
console.error(f"Unable to read from pyproject.toml due to {ex}")
|
|
741
|
+
raise typer.Exit(code=1) from ex
|
|
742
|
+
|
|
743
|
+
try:
|
|
744
|
+
project = pyproject_toml.get("project", {})
|
|
745
|
+
if not project:
|
|
746
|
+
console.error("The project section not found in pyproject.toml")
|
|
747
|
+
raise typer.Exit(code=1)
|
|
748
|
+
console.print(
|
|
749
|
+
f'Double check the information before publishing: {project["name"]} version {project["version"]}'
|
|
750
|
+
)
|
|
751
|
+
except KeyError as ke:
|
|
752
|
+
console.error(f"The pyproject.toml is possibly ill-formed due to {ke}")
|
|
753
|
+
raise typer.Exit(code=1) from ke
|
|
754
|
+
|
|
755
|
+
console.print("Update or enter to keep the current information.")
|
|
756
|
+
project["description"] = console.ask(
|
|
757
|
+
"short description", default=project.get("description", "")
|
|
758
|
+
)
|
|
759
|
+
# PyPI only shows the first author.
|
|
760
|
+
author = project.get("authors", [{}])[0]
|
|
761
|
+
author["name"] = console.ask(f"Author Name", default=author.get("name", ""))
|
|
762
|
+
author["email"] = console.ask(f"Author Email", default=author.get("email", ""))
|
|
763
|
+
|
|
764
|
+
console.print(f'Current keywords are: {project.get("keywords") or []}')
|
|
765
|
+
keyword_action = console.ask(
|
|
766
|
+
"Keep, replace or append?", choices=["k", "r", "a"], default="k"
|
|
767
|
+
)
|
|
768
|
+
new_keywords = []
|
|
769
|
+
if keyword_action == "r":
|
|
770
|
+
new_keywords = (
|
|
771
|
+
_process_entered_list(
|
|
772
|
+
console.ask("Enter new set of keywords separated by commas")
|
|
773
|
+
)
|
|
774
|
+
or []
|
|
775
|
+
)
|
|
776
|
+
elif keyword_action == "a":
|
|
777
|
+
new_keywords = (
|
|
778
|
+
_process_entered_list(
|
|
779
|
+
console.ask("Enter new set of keywords separated by commas")
|
|
780
|
+
)
|
|
781
|
+
or []
|
|
782
|
+
)
|
|
783
|
+
project["keywords"] = project.get("keywords", []) + new_keywords
|
|
784
|
+
|
|
785
|
+
if not project.get("urls"):
|
|
786
|
+
project["urls"] = {}
|
|
787
|
+
project["urls"]["homepage"] = console.ask(
|
|
788
|
+
"homepage URL", default=project["urls"].get("homepage", "")
|
|
789
|
+
)
|
|
790
|
+
project["urls"]["source"] = console.ask(
|
|
791
|
+
"source code URL", default=project["urls"].get("source", "")
|
|
792
|
+
)
|
|
793
|
+
pyproject_toml["project"] = project
|
|
794
|
+
try:
|
|
795
|
+
with open(CustomComponents.PYPROJECT_TOML, "w") as f:
|
|
796
|
+
tomlkit.dump(pyproject_toml, f)
|
|
797
|
+
except (OSError, TOMLKitError) as ex:
|
|
798
|
+
console.error(f"Unable to read from pyproject.toml due to {ex}")
|
|
799
|
+
raise typer.Exit(code=1) from ex
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
def _collect_details_for_gallery():
|
|
803
|
+
"""Helper to collect details on the custom component to be included in the gallery.
|
|
804
|
+
|
|
805
|
+
Raises:
|
|
806
|
+
Exit: If pyproject.toml file is ill-formed or the request to the backend services fails.
|
|
807
|
+
"""
|
|
808
|
+
from reflex.reflex import _login
|
|
809
|
+
|
|
810
|
+
console.rule("[bold]Authentication with Reflex Services")
|
|
811
|
+
console.print("First let's log in to Reflex backend services.")
|
|
812
|
+
access_token = _login()
|
|
813
|
+
|
|
814
|
+
console.rule("[bold]Custom Component Information")
|
|
815
|
+
params = {}
|
|
816
|
+
package_name = None
|
|
817
|
+
try:
|
|
818
|
+
with open(CustomComponents.PYPROJECT_TOML, "rb") as f:
|
|
819
|
+
project_toml = tomlkit.parse(f.read())
|
|
820
|
+
package_name = project_toml.get("project", {})["name"]
|
|
821
|
+
except (OSError, TOMLKitError, KeyError) as ex:
|
|
822
|
+
console.debug(
|
|
823
|
+
f"Unable to read from pyproject.toml in current directory due to {ex}"
|
|
824
|
+
)
|
|
825
|
+
package_name = console.ask("[ Published python package name ]")
|
|
826
|
+
console.print(f"[ Custom component package name ] : {package_name}")
|
|
827
|
+
params["package_name"] = package_name
|
|
828
|
+
|
|
829
|
+
# Check the backend services if the user is allowed to update information of this package is already shared.
|
|
830
|
+
try:
|
|
831
|
+
console.debug(
|
|
832
|
+
f"Checking if user has permission to upsert information for {package_name} by POST."
|
|
833
|
+
)
|
|
834
|
+
# Send a POST request to achieve two things at once:
|
|
835
|
+
# 1. Check if the package is already shared by the user. If not, the backend will return 403.
|
|
836
|
+
# 2. If this package is not shared before, this request records the package name in the backend.
|
|
837
|
+
response = httpx.post(
|
|
838
|
+
POST_CUSTOM_COMPONENTS_GALLERY_ENDPOINT,
|
|
839
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
|
840
|
+
data=params,
|
|
841
|
+
)
|
|
842
|
+
if response.status_code == httpx.codes.FORBIDDEN:
|
|
843
|
+
console.error(
|
|
844
|
+
f"{package_name} is owned by another user. Unable to update the information for it."
|
|
845
|
+
)
|
|
846
|
+
raise typer.Exit(code=1)
|
|
847
|
+
response.raise_for_status()
|
|
848
|
+
except httpx.HTTPError as he:
|
|
849
|
+
console.error(f"Unable to complete request due to {he}.")
|
|
850
|
+
raise typer.Exit(code=1) from he
|
|
851
|
+
|
|
852
|
+
display_name = (
|
|
853
|
+
console.ask("[ Friendly display name for your component ] (enter to skip)")
|
|
854
|
+
or None
|
|
855
|
+
)
|
|
856
|
+
if display_name:
|
|
857
|
+
params["display_name"] = display_name
|
|
858
|
+
|
|
859
|
+
files = []
|
|
860
|
+
if (image_file_and_extension := _get_file_from_prompt_in_loop()) is not None:
|
|
861
|
+
files.append(
|
|
862
|
+
("files", (image_file_and_extension[1], image_file_and_extension[0]))
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
demo_url = None
|
|
866
|
+
while True:
|
|
867
|
+
demo_url = (
|
|
868
|
+
console.ask(
|
|
869
|
+
"[ Full URL of deployed demo app, e.g. `https://my-app.reflex.run` ] (enter to skip)"
|
|
870
|
+
)
|
|
871
|
+
or None
|
|
872
|
+
)
|
|
873
|
+
if _validate_url_with_protocol_prefix(demo_url):
|
|
874
|
+
break
|
|
875
|
+
if demo_url:
|
|
876
|
+
params["demo_url"] = demo_url
|
|
877
|
+
|
|
878
|
+
# Now send the post request to Reflex backend services.
|
|
879
|
+
try:
|
|
880
|
+
console.debug(f"Sending custom component data: {params}")
|
|
881
|
+
response = httpx.post(
|
|
882
|
+
POST_CUSTOM_COMPONENTS_GALLERY_ENDPOINT,
|
|
883
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
|
884
|
+
data=params,
|
|
885
|
+
files=files,
|
|
886
|
+
timeout=POST_CUSTOM_COMPONENTS_GALLERY_TIMEOUT,
|
|
887
|
+
)
|
|
888
|
+
response.raise_for_status()
|
|
889
|
+
|
|
890
|
+
except httpx.HTTPError as he:
|
|
891
|
+
console.error(f"Unable to complete request due to {he}.")
|
|
892
|
+
raise typer.Exit(code=1) from he
|
|
893
|
+
|
|
894
|
+
console.info("Custom component information successfully shared!")
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
def _validate_url_with_protocol_prefix(url: str | None) -> bool:
|
|
898
|
+
"""Validate the URL with protocol prefix. Empty string is acceptable.
|
|
899
|
+
|
|
900
|
+
Args:
|
|
901
|
+
url: the URL string to check.
|
|
902
|
+
|
|
903
|
+
Returns:
|
|
904
|
+
Whether the entered URL is acceptable.
|
|
905
|
+
"""
|
|
906
|
+
return not url or (url.startswith("http://") or url.startswith("https://"))
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
def _get_file_from_prompt_in_loop() -> Tuple[bytes, str] | None:
|
|
910
|
+
image_file = file_extension = None
|
|
911
|
+
while image_file is None:
|
|
912
|
+
image_filepath = console.ask(
|
|
913
|
+
f"Upload a preview image of your demo app (enter to skip)"
|
|
914
|
+
)
|
|
915
|
+
if not image_filepath:
|
|
916
|
+
break
|
|
917
|
+
file_extension = image_filepath.split(".")[-1]
|
|
918
|
+
try:
|
|
919
|
+
with open(image_filepath, "rb") as f:
|
|
920
|
+
image_file = f.read()
|
|
921
|
+
return image_file, file_extension
|
|
922
|
+
except OSError as ose:
|
|
923
|
+
console.error(f"Unable to read the {file_extension} file due to {ose}")
|
|
924
|
+
raise typer.Exit(code=1) from ose
|
|
925
|
+
|
|
926
|
+
console.debug(f"File extension detected: {file_extension}")
|
|
927
|
+
return None
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
@custom_components_cli.command(name="share")
|
|
931
|
+
def share_more_detail(
|
|
932
|
+
loglevel: constants.LogLevel = typer.Option(
|
|
933
|
+
config.loglevel, help="The log level to use."
|
|
934
|
+
),
|
|
935
|
+
):
|
|
936
|
+
"""Collect more details on the published package for gallery.
|
|
937
|
+
|
|
938
|
+
Args:
|
|
939
|
+
loglevel: The log level to use.
|
|
940
|
+
"""
|
|
941
|
+
console.set_log_level(loglevel)
|
|
942
|
+
|
|
943
|
+
_collect_details_for_gallery()
|