Add support for custom components starter (#2314)
This commit is contained in:
parent
c465b94016
commit
4f9cdd6472
@ -5,7 +5,7 @@ branch = true
|
||||
[report]
|
||||
show_missing = true
|
||||
# TODO bump back to 79
|
||||
fail_under = 69
|
||||
fail_under = 68
|
||||
precision = 2
|
||||
|
||||
# Regexes for lines to exclude from consideration
|
||||
|
181
poetry.lock
generated
181
poetry.lock
generated
@ -1,10 +1,9 @@
|
||||
# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.13.1"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -26,7 +25,6 @@ tz = ["backports.zoneinfo"]
|
||||
name = "anyio"
|
||||
version = "4.3.0"
|
||||
description = "High level compatibility layer for multiple asynchronous event loop implementations"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -49,7 +47,6 @@ trio = ["trio (>=0.23)"]
|
||||
name = "async-timeout"
|
||||
version = "4.0.3"
|
||||
description = "Timeout context manager for asyncio programs"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -61,7 +58,6 @@ files = [
|
||||
name = "asynctest"
|
||||
version = "0.13.0"
|
||||
description = "Enhance the standard unittest package with features for testing asyncio libraries"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
@ -73,7 +69,6 @@ files = [
|
||||
name = "attrs"
|
||||
version = "23.2.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -93,7 +88,6 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p
|
||||
name = "bidict"
|
||||
version = "0.23.1"
|
||||
description = "The bidirectional mapping library for Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -105,7 +99,6 @@ files = [
|
||||
name = "black"
|
||||
version = "22.12.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -137,11 +130,34 @@ d = ["aiohttp (>=3.7.4)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "build"
|
||||
version = "1.0.3"
|
||||
description = "A simple, correct Python build frontend"
|
||||
optional = false
|
||||
python-versions = ">= 3.7"
|
||||
files = [
|
||||
{file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"},
|
||||
{file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "os_name == \"nt\""}
|
||||
importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""}
|
||||
packaging = ">=19.0"
|
||||
pyproject_hooks = "*"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.08.17)", "sphinx (>=7.0,<8.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)", "sphinx-issues (>=3.0.0)"]
|
||||
test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "setuptools (>=56.0.0)", "setuptools (>=67.8.0)", "wheel (>=0.36.0)"]
|
||||
typing = ["importlib-metadata (>=5.1)", "mypy (>=1.5.0,<1.6.0)", "tomli", "typing-extensions (>=3.7.4.3)"]
|
||||
virtualenv = ["virtualenv (>=20.0.35)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2024.2.2"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -153,7 +169,6 @@ files = [
|
||||
name = "cffi"
|
||||
version = "1.16.0"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -218,7 +233,6 @@ pycparser = "*"
|
||||
name = "cfgv"
|
||||
version = "3.4.0"
|
||||
description = "Validate configuration and produce human readable error messages."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -230,7 +244,6 @@ files = [
|
||||
name = "charset-normalizer"
|
||||
version = "3.3.2"
|
||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
@ -330,7 +343,6 @@ files = [
|
||||
name = "click"
|
||||
version = "8.1.7"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -345,7 +357,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
name = "cloudpickle"
|
||||
version = "2.2.1"
|
||||
description = "Extended pickling support for Python objects"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -357,7 +368,6 @@ files = [
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
@ -369,7 +379,6 @@ files = [
|
||||
name = "coverage"
|
||||
version = "7.4.1"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -437,7 +446,6 @@ toml = ["tomli"]
|
||||
name = "darglint"
|
||||
version = "1.8.1"
|
||||
description = "A utility for ensuring Google-style docstrings stay up to date with the source code."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4.0"
|
||||
files = [
|
||||
@ -449,7 +457,6 @@ files = [
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
description = "Distribution utilities"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -461,7 +468,6 @@ files = [
|
||||
name = "distro"
|
||||
version = "1.9.0"
|
||||
description = "Distro - an OS platform information API"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -473,7 +479,6 @@ files = [
|
||||
name = "docopt"
|
||||
version = "0.6.2"
|
||||
description = "Pythonic argument parser, that will make you smile"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -484,7 +489,6 @@ files = [
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.0"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -499,7 +503,6 @@ test = ["pytest (>=6)"]
|
||||
name = "fastapi"
|
||||
version = "0.96.1"
|
||||
description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -521,7 +524,6 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6
|
||||
name = "filelock"
|
||||
version = "3.13.1"
|
||||
description = "A platform independent file lock."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -538,7 +540,6 @@ typing = ["typing-extensions (>=4.8)"]
|
||||
name = "greenlet"
|
||||
version = "3.0.3"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -610,7 +611,6 @@ test = ["objgraph", "psutil"]
|
||||
name = "gunicorn"
|
||||
version = "20.1.0"
|
||||
description = "WSGI HTTP Server for UNIX"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
@ -631,7 +631,6 @@ tornado = ["tornado (>=0.2)"]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -643,7 +642,6 @@ files = [
|
||||
name = "httpcore"
|
||||
version = "1.0.3"
|
||||
description = "A minimal low-level HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -658,14 +656,13 @@ h11 = ">=0.13,<0.15"
|
||||
[package.extras]
|
||||
asyncio = ["anyio (>=4.0,<5.0)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||
socks = ["socksio (==1.*)"]
|
||||
trio = ["trio (>=0.22.0,<0.24.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.25.2"
|
||||
description = "The next generation HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -676,21 +673,20 @@ files = [
|
||||
[package.dependencies]
|
||||
anyio = "*"
|
||||
certifi = "*"
|
||||
httpcore = ">=1.0.0,<2.0.0"
|
||||
httpcore = "==1.*"
|
||||
idna = "*"
|
||||
sniffio = "*"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli", "brotlicffi"]
|
||||
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"]
|
||||
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
|
||||
http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (>=1.0.0,<2.0.0)"]
|
||||
socks = ["socksio (==1.*)"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.35"
|
||||
description = "File identification library for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -705,7 +701,6 @@ license = ["ukkonen"]
|
||||
name = "idna"
|
||||
version = "3.6"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
@ -717,7 +712,6 @@ files = [
|
||||
name = "importlib-metadata"
|
||||
version = "7.0.1"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -737,7 +731,6 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs
|
||||
name = "importlib-resources"
|
||||
version = "6.1.1"
|
||||
description = "Read resources from Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -756,7 +749,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)",
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -768,7 +760,6 @@ files = [
|
||||
name = "jinja2"
|
||||
version = "3.1.3"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -786,7 +777,6 @@ i18n = ["Babel (>=2.7)"]
|
||||
name = "mako"
|
||||
version = "1.3.2"
|
||||
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -806,7 +796,6 @@ testing = ["pytest"]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
description = "Python port of markdown-it. Markdown parsing, done right!"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -831,7 +820,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
|
||||
name = "markupsafe"
|
||||
version = "2.1.5"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -901,7 +889,6 @@ files = [
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
description = "Markdown URL utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -913,7 +900,6 @@ files = [
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
@ -925,7 +911,6 @@ files = [
|
||||
name = "nodeenv"
|
||||
version = "1.8.0"
|
||||
description = "Node.js virtual environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
|
||||
files = [
|
||||
@ -940,7 +925,6 @@ setuptools = "*"
|
||||
name = "numpy"
|
||||
version = "1.24.4"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -978,7 +962,6 @@ files = [
|
||||
name = "numpy"
|
||||
version = "1.26.4"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
@ -1024,7 +1007,6 @@ files = [
|
||||
name = "outcome"
|
||||
version = "1.3.0.post0"
|
||||
description = "Capture the outcome of Python function calls."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1039,7 +1021,6 @@ attrs = ">=19.2.0"
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1051,7 +1032,6 @@ files = [
|
||||
name = "pandas"
|
||||
version = "1.5.3"
|
||||
description = "Powerful data structures for data analysis, time series, and statistics"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1096,7 +1076,6 @@ test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
|
||||
name = "pandas"
|
||||
version = "2.2.0"
|
||||
description = "Powerful data structures for data analysis, time series, and statistics"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
@ -1169,7 +1148,6 @@ xml = ["lxml (>=4.9.2)"]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1181,7 +1159,6 @@ files = [
|
||||
name = "pillow"
|
||||
version = "10.2.0"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1267,7 +1244,6 @@ xmp = ["defusedxml"]
|
||||
name = "pipdeptree"
|
||||
version = "2.14.0"
|
||||
description = "Command line utility to show dependency tree of packages."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1283,7 +1259,6 @@ test = ["covdefaults (>=2.3)", "diff-cover (>=8.0.1)", "pip (>=23.3.1)", "pytest
|
||||
name = "pipreqs"
|
||||
version = "0.4.13"
|
||||
description = "Pip requirements.txt generator based on imports in project"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1299,7 +1274,6 @@ yarg = "*"
|
||||
name = "platformdirs"
|
||||
version = "3.11.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1315,7 +1289,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co
|
||||
name = "plotly"
|
||||
version = "5.19.0"
|
||||
description = "An open-source, interactive data visualization library for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1331,7 +1304,6 @@ tenacity = ">=6.2.0"
|
||||
name = "pluggy"
|
||||
version = "1.4.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1347,7 +1319,6 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
name = "pre-commit"
|
||||
version = "3.5.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1366,7 +1337,6 @@ virtualenv = ">=20.10.0"
|
||||
name = "psutil"
|
||||
version = "5.9.8"
|
||||
description = "Cross-platform lib for process and system monitoring in Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
|
||||
files = [
|
||||
@ -1395,7 +1365,6 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
|
||||
name = "py-cpuinfo"
|
||||
version = "9.0.0"
|
||||
description = "Get CPU info with pure Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -1407,7 +1376,6 @@ files = [
|
||||
name = "pycparser"
|
||||
version = "2.21"
|
||||
description = "C parser in Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
@ -1419,7 +1387,6 @@ files = [
|
||||
name = "pydantic"
|
||||
version = "1.10.14"
|
||||
description = "Data validation and settings management using python type hints"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1472,7 +1439,6 @@ email = ["email-validator (>=1.0.3)"]
|
||||
name = "pygments"
|
||||
version = "2.17.2"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1484,11 +1450,24 @@ files = [
|
||||
plugins = ["importlib-metadata"]
|
||||
windows-terminal = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyproject-hooks"
|
||||
version = "1.0.0"
|
||||
description = "Wrappers to call pyproject.toml-based build backend hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"},
|
||||
{file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.334"
|
||||
description = "Command line wrapper for pyright"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1507,7 +1486,6 @@ dev = ["twine (>=3.4.1)"]
|
||||
name = "pysocks"
|
||||
version = "1.7.1"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
files = [
|
||||
@ -1520,7 +1498,6 @@ files = [
|
||||
name = "pytest"
|
||||
version = "7.4.4"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1543,7 +1520,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no
|
||||
name = "pytest-asyncio"
|
||||
version = "0.20.3"
|
||||
description = "Pytest support for asyncio"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1562,7 +1538,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy
|
||||
name = "pytest-benchmark"
|
||||
version = "4.0.0"
|
||||
description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1583,7 +1558,6 @@ histogram = ["pygal", "pygaljs"]
|
||||
name = "pytest-cov"
|
||||
version = "4.1.0"
|
||||
description = "Pytest plugin for measuring coverage."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1602,7 +1576,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
|
||||
name = "pytest-mock"
|
||||
version = "3.12.0"
|
||||
description = "Thin-wrapper around the mock package for easier use with pytest"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1620,7 +1593,6 @@ dev = ["pre-commit", "pytest-asyncio", "tox"]
|
||||
name = "python-dateutil"
|
||||
version = "2.8.2"
|
||||
description = "Extensions to the standard Python datetime module"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
files = [
|
||||
@ -1635,7 +1607,6 @@ six = ">=1.5"
|
||||
name = "python-engineio"
|
||||
version = "4.9.0"
|
||||
description = "Engine.IO server and client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -1655,7 +1626,6 @@ docs = ["sphinx"]
|
||||
name = "python-multipart"
|
||||
version = "0.0.5"
|
||||
description = "A streaming multipart parser for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -1669,7 +1639,6 @@ six = ">=1.4.0"
|
||||
name = "python-socketio"
|
||||
version = "5.11.1"
|
||||
description = "Socket.IO server and client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1690,7 +1659,6 @@ docs = ["sphinx"]
|
||||
name = "pytz"
|
||||
version = "2024.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -1702,7 +1670,6 @@ files = [
|
||||
name = "pyyaml"
|
||||
version = "6.0.1"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -1752,7 +1719,6 @@ files = [
|
||||
name = "redis"
|
||||
version = "4.6.0"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1771,7 +1737,6 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
|
||||
name = "reflex-hosting-cli"
|
||||
version = "0.1.8"
|
||||
description = "Reflex Hosting CLI"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8,<4.0"
|
||||
files = [
|
||||
@ -1796,7 +1761,6 @@ websockets = ">=10.4"
|
||||
name = "requests"
|
||||
version = "2.31.0"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1818,7 +1782,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
name = "rich"
|
||||
version = "13.7.0"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
@ -1838,7 +1801,6 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||
name = "ruff"
|
||||
version = "0.0.244"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1864,7 +1826,6 @@ files = [
|
||||
name = "selenium"
|
||||
version = "4.18.0"
|
||||
description = ""
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -1881,26 +1842,24 @@ urllib3 = {version = ">=1.26,<3", extras = ["socks"]}
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "69.1.0"
|
||||
version = "69.1.1"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"},
|
||||
{file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"},
|
||||
{file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"},
|
||||
{file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "simple-websocket"
|
||||
version = "1.0.0"
|
||||
description = "Simple WebSocket server and client for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -1918,7 +1877,6 @@ docs = ["sphinx"]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
@ -1930,7 +1888,6 @@ files = [
|
||||
name = "sniffio"
|
||||
version = "1.3.0"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -1942,7 +1899,6 @@ files = [
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -1954,7 +1910,6 @@ files = [
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.27"
|
||||
description = "Database Abstraction Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2042,7 +1997,6 @@ sqlcipher = ["sqlcipher3_binary"]
|
||||
name = "sqlmodel"
|
||||
version = "0.0.14"
|
||||
description = "SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7,<4.0"
|
||||
files = [
|
||||
@ -2058,7 +2012,6 @@ SQLAlchemy = ">=2.0.0,<2.1.0"
|
||||
name = "starlette"
|
||||
version = "0.27.0"
|
||||
description = "The little ASGI library that shines."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2077,7 +2030,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam
|
||||
name = "starlette-admin"
|
||||
version = "0.9.0"
|
||||
description = "Fast, beautiful and extensible administrative interface framework for Starlette/FastApi applications"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2100,7 +2052,6 @@ test = ["aiomysql (>=0.1.1,<0.2.0)", "aiosqlite (>=0.17.0,<0.20.0)", "arrow (>=1
|
||||
name = "tabulate"
|
||||
version = "0.9.0"
|
||||
description = "Pretty-print tabular data"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2115,7 +2066,6 @@ widechars = ["wcwidth"]
|
||||
name = "tenacity"
|
||||
version = "8.2.3"
|
||||
description = "Retry code until it succeeds"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2130,7 +2080,6 @@ doc = ["reno", "sphinx", "tornado (>=4.5)"]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
files = [
|
||||
@ -2142,7 +2091,6 @@ files = [
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2154,7 +2102,6 @@ files = [
|
||||
name = "trio"
|
||||
version = "0.24.0"
|
||||
description = "A friendly Python library for async concurrency and I/O"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2175,7 +2122,6 @@ sortedcontainers = "*"
|
||||
name = "trio-websocket"
|
||||
version = "0.11.1"
|
||||
description = "WebSocket library for Trio"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2192,7 +2138,6 @@ wsproto = ">=0.14"
|
||||
name = "typer"
|
||||
version = "0.9.0"
|
||||
description = "Typer, build great CLIs. Easy to code. Based on Python type hints."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -2214,7 +2159,6 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.
|
||||
name = "types-tabulate"
|
||||
version = "0.9.0.20240106"
|
||||
description = "Typing stubs for tabulate"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2226,7 +2170,6 @@ files = [
|
||||
name = "typing-extensions"
|
||||
version = "4.9.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2238,7 +2181,6 @@ files = [
|
||||
name = "tzdata"
|
||||
version = "2024.1"
|
||||
description = "Provider of IANA time zone data"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
@ -2250,7 +2192,6 @@ files = [
|
||||
name = "urllib3"
|
||||
version = "2.2.1"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2271,7 +2212,6 @@ zstd = ["zstandard (>=0.18.0)"]
|
||||
name = "uvicorn"
|
||||
version = "0.20.0"
|
||||
description = "The lightning-fast ASGI server."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2290,7 +2230,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
|
||||
name = "uvicorn"
|
||||
version = "0.24.0.post1"
|
||||
description = "The lightning-fast ASGI server."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2309,7 +2248,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
|
||||
name = "virtualenv"
|
||||
version = "20.25.0"
|
||||
description = "Virtual Python Environment builder"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2330,7 +2268,6 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
|
||||
name = "watchdog"
|
||||
version = "2.3.1"
|
||||
description = "Filesystem events monitoring"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -2371,7 +2308,6 @@ watchmedo = ["PyYAML (>=3.10)"]
|
||||
name = "watchfiles"
|
||||
version = "0.19.0"
|
||||
description = "Simple, modern and high performance file watching and code reload in python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
@ -2406,7 +2342,6 @@ anyio = ">=3.0.0"
|
||||
name = "websockets"
|
||||
version = "12.0"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2484,11 +2419,24 @@ files = [
|
||||
{file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wheel"
|
||||
version = "0.42.0"
|
||||
description = "A built-package format for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "wheel-0.42.0-py3-none-any.whl", hash = "sha256:177f9c9b0d45c47873b619f5b650346d632cdc35fb5e4d25058e09c9e581433d"},
|
||||
{file = "wheel-0.42.0.tar.gz", hash = "sha256:c45be39f7882c9d34243236f2d63cbd58039e360f85d0913425fbd7ceea617a8"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest (>=6.0.0)", "setuptools (>=65)"]
|
||||
|
||||
[[package]]
|
||||
name = "wrapt"
|
||||
version = "1.16.0"
|
||||
description = "Module for decorators, wrappers and monkey patching."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
@ -2568,7 +2516,6 @@ files = [
|
||||
name = "wsproto"
|
||||
version = "1.2.0"
|
||||
description = "WebSockets state-machine based protocol implementation"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
@ -2583,7 +2530,6 @@ h11 = ">=0.9.0,<1"
|
||||
name = "yarg"
|
||||
version = "0.1.9"
|
||||
description = "A semi hard Cornish cheese, also queries PyPI (PyPI client)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
@ -2598,7 +2544,6 @@ requests = "*"
|
||||
name = "zipp"
|
||||
version = "3.17.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
@ -2613,4 +2558,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.8"
|
||||
content-hash = "c22317cf6beac82268e73619e984788895d258bb7b7983eacdfbf6093c419dc3"
|
||||
content-hash = "840901e82824445e708eb44ae7f237b286946db2dd3332740c6d1e5e891fb84d"
|
||||
|
@ -59,6 +59,9 @@ packaging = "^23.1"
|
||||
pipdeptree = "^2.13.0"
|
||||
reflex-hosting-cli = ">=0.1.2"
|
||||
charset-normalizer = "^3.3.2"
|
||||
wheel = "^0.42.0"
|
||||
build = "^1.0.3"
|
||||
setuptools = "^69.1.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.1.2"
|
||||
|
@ -0,0 +1,9 @@
|
||||
# {{ module_name }}
|
||||
|
||||
A Reflex custom component {{ module_name }}.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install {{ package_name }}
|
||||
```
|
@ -0,0 +1 @@
|
||||
from .{{ module_name }} import *
|
36
reflex/.templates/jinja/custom_components/demo_app.py.jinja2
Normal file
36
reflex/.templates/jinja/custom_components/demo_app.py.jinja2
Normal file
@ -0,0 +1,36 @@
|
||||
"""Welcome to Reflex! This file showcases the custom component in a basic app."""
|
||||
|
||||
from rxconfig import config
|
||||
|
||||
import reflex as rx
|
||||
|
||||
from {{ custom_component_module_dir }} import {{ module_name }}
|
||||
|
||||
filename = f"{config.app_name}/{config.app_name}.py"
|
||||
|
||||
|
||||
class State(rx.State):
|
||||
"""The app state."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def index() -> rx.Component:
|
||||
return rx.center(
|
||||
rx.theme_panel(),
|
||||
rx.vstack(
|
||||
rx.heading("Welcome to Reflex!", size="9"),
|
||||
rx.text("Test your custom component by editing ", rx.code(filename)),
|
||||
{{ module_name }}(),
|
||||
align="center",
|
||||
spacing="7",
|
||||
font_size="2em",
|
||||
),
|
||||
height="100vh",
|
||||
)
|
||||
|
||||
|
||||
# Add state and page to the app.
|
||||
app = rx.App()
|
||||
app.add_page(index)
|
||||
|
@ -0,0 +1,35 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools",
|
||||
"wheel",
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "{{ package_name }}"
|
||||
version = "0.0.1"
|
||||
description = "Reflex custom component {{ module_name }}"
|
||||
readme = "README.md"
|
||||
license = { text = "Apache-2.0" }
|
||||
requires-python = ">=3.8"
|
||||
authors = [{ name = "Your Name", email = "YOUREMAIL@domain.com" }]
|
||||
keywords = [
|
||||
"reflex",
|
||||
"reflex-custom-components"]
|
||||
|
||||
dependencies = [
|
||||
"reflex>=0.4.2"
|
||||
]
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com"
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["build", "twine"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["custom_components"]
|
57
reflex/.templates/jinja/custom_components/src.py.jinja2
Normal file
57
reflex/.templates/jinja/custom_components/src.py.jinja2
Normal file
@ -0,0 +1,57 @@
|
||||
"""Reflex custom component {{ component_class_name }}."""
|
||||
|
||||
# For wrapping react guide, visit https://reflex.dev/docs/wrapping-react/overview/
|
||||
|
||||
import reflex as rx
|
||||
|
||||
# Some libraries you may want to wrap may require dynamic imports.
|
||||
# This is because they they may not be compatible with Server-Side Rendering (SSR).
|
||||
# To handle this in Reflex all you need to do is subclass NoSSRComponent instead.
|
||||
# For example:
|
||||
# from reflex.components.component import NoSSRComponent
|
||||
# class {{ component_class_name }}(NoSSRComponent):
|
||||
# pass
|
||||
|
||||
|
||||
class {{ component_class_name }}(rx.Component):
|
||||
"""{{ component_class_name }} component."""
|
||||
|
||||
# The React library to wrap.
|
||||
library = "Fill-Me"
|
||||
|
||||
# The React component tag.
|
||||
tag = "Fill-Me"
|
||||
|
||||
# If the tag is the default export from the module, you must set is_default = True.
|
||||
# This is normally used when components don't have curly braces around them when importing.
|
||||
# is_default = True
|
||||
|
||||
# If you are wrapping another components with the same tag as a component in your project
|
||||
# you can use aliases to differentiate between them and avoid naming conflicts.
|
||||
# alias = "Other{{ component_class_name }}"
|
||||
|
||||
# The props of the React component.
|
||||
# Note: when Reflex compiles the component to Javascript,
|
||||
# `snake_case` property names are automatically formatted as `camelCase`.
|
||||
# The prop names may be defined in `camelCase` as well.
|
||||
# some_prop: rx.Var[str] = "some default value"
|
||||
# some_other_prop: rx.Var[int] = 1
|
||||
|
||||
# By default Reflex will install the library you have specified in the library property.
|
||||
# However, sometimes you may need to install other libraries to use a component.
|
||||
# In this case you can use the lib_dependencies property to specify other libraries to install.
|
||||
# lib_dependencies: list[str] = []
|
||||
|
||||
# Event triggers, I did not understand the wording of the doc.
|
||||
# def get_event_triggers(self) -> dict[str, Any]:
|
||||
# return {
|
||||
# **super().get_event_triggers(),
|
||||
# "on_change": lambda e0: [e0],
|
||||
# }
|
||||
|
||||
# To add custom code to your component
|
||||
# def _get_custom_code(self) -> str:
|
||||
# return "const customCode = 'customCode';"
|
||||
|
||||
|
||||
{{ module_name }} = {{ component_class_name }}.create
|
@ -98,3 +98,20 @@ STYLE = get_template("web/styles/styles.css.jinja2")
|
||||
|
||||
# Code that generate the package json file
|
||||
PACKAGE_JSON = get_template("web/package.json.jinja2")
|
||||
|
||||
# Code that generate the pyproject.toml file for custom components
|
||||
CUSTOM_COMPONENTS_PYPROJECT_TOML = get_template(
|
||||
"custom_components/pyproject.toml.jinja2"
|
||||
)
|
||||
|
||||
# Code that generates the README file for custom components
|
||||
CUSTOM_COMPONENTS_README = get_template("custom_components/README.md.jinja2")
|
||||
|
||||
# Code that generates the source file for custom components
|
||||
CUSTOM_COMPONENTS_SOURCE = get_template("custom_components/src.py.jinja2")
|
||||
|
||||
# Code that generates the init file for custom components
|
||||
CUSTOM_COMPONENTS_INIT_FILE = get_template("custom_components/__init__.py.jinja2")
|
||||
|
||||
# Code that generates the demo app main py file for testing custom components
|
||||
CUSTOM_COMPONENTS_DEMO_APP = get_template("custom_components/demo_app.py.jinja2")
|
||||
|
@ -40,6 +40,9 @@ from .config import (
|
||||
GitIgnore,
|
||||
RequirementsTxt,
|
||||
)
|
||||
from .custom_components import (
|
||||
CustomComponents,
|
||||
)
|
||||
from .event import Endpoint, EventTriggers, SocketEvent
|
||||
from .installer import (
|
||||
Bun,
|
||||
@ -67,6 +70,7 @@ __ALL__ = [
|
||||
Config,
|
||||
COOKIES,
|
||||
ComponentName,
|
||||
CustomComponents,
|
||||
DefaultPage,
|
||||
Dirs,
|
||||
Endpoint,
|
||||
|
30
reflex/constants/custom_components.py
Normal file
30
reflex/constants/custom_components.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""Constants for the custom components."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
class CustomComponents(SimpleNamespace):
|
||||
"""Constants for the custom components."""
|
||||
|
||||
# The name of the custom components source directory.
|
||||
SRC_DIR = "custom_components"
|
||||
# The name of the custom components pyproject.toml file.
|
||||
PYPROJECT_TOML = "pyproject.toml"
|
||||
# The name of the custom components package README file.
|
||||
PACKAGE_README = "README.md"
|
||||
# The name of the custom components package .gitignore file.
|
||||
PACKAGE_GITIGNORE = ".gitignore"
|
||||
# The name of the distribution directory as result of a build.
|
||||
DIST_DIR = "dist"
|
||||
# The name of the init file.
|
||||
INIT_FILE = "__init__.py"
|
||||
# Suffixes for the distribution files.
|
||||
DISTRIBUTION_FILE_SUFFIXES = [".tar.gz", ".whl"]
|
||||
# The name to the URL of python package repositories.
|
||||
REPO_URLS = {
|
||||
# Note: the trailing slash is required for below URLs.
|
||||
"pypi": "https://upload.pypi.org/legacy/",
|
||||
"testpypi": "https://test.pypi.org/legacy/",
|
||||
}
|
1
reflex/custom_components/__init__.py
Normal file
1
reflex/custom_components/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""The Reflex custom components."""
|
565
reflex/custom_components/custom_components.py
Normal file
565
reflex/custom_components/custom_components.py
Normal file
@ -0,0 +1,565 @@
|
||||
"""CLI for creating custom components."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
|
||||
from reflex import constants
|
||||
from reflex.config import get_config
|
||||
from reflex.constants import CustomComponents
|
||||
from reflex.utils import console
|
||||
|
||||
config = get_config()
|
||||
custom_components_cli = typer.Typer()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def set_directory(working_directory: str):
|
||||
"""Context manager that sets the working directory.
|
||||
|
||||
Args:
|
||||
working_directory: The working directory to change to.
|
||||
|
||||
Yields:
|
||||
Yield to the caller to perform operations in the working directory.
|
||||
"""
|
||||
current_directory = os.getcwd()
|
||||
try:
|
||||
os.chdir(working_directory)
|
||||
yield
|
||||
finally:
|
||||
os.chdir(current_directory)
|
||||
|
||||
|
||||
def _create_package_config(module_name: str, package_name: str):
|
||||
"""Create a package config pyproject.toml file.
|
||||
|
||||
Args:
|
||||
module_name: The name of the module.
|
||||
package_name: The name of the package typically constructed with `reflex-` prefix and a meaningful library name.
|
||||
"""
|
||||
from reflex.compiler import templates
|
||||
|
||||
with open(CustomComponents.PYPROJECT_TOML, "w") as f:
|
||||
f.write(
|
||||
templates.CUSTOM_COMPONENTS_PYPROJECT_TOML.render(
|
||||
module_name=module_name, package_name=package_name
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _create_readme(module_name: str, package_name: str):
|
||||
"""Create a package README file.
|
||||
|
||||
Args:
|
||||
module_name: The name of the module.
|
||||
package_name: The name of the python package to be published.
|
||||
"""
|
||||
from reflex.compiler import templates
|
||||
|
||||
with open(CustomComponents.PACKAGE_README, "w") as f:
|
||||
f.write(
|
||||
templates.CUSTOM_COMPONENTS_README.render(
|
||||
module_name=module_name,
|
||||
package_name=package_name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _write_source_and_init_py(
|
||||
custom_component_src_dir: str,
|
||||
component_class_name: str,
|
||||
module_name: str,
|
||||
):
|
||||
"""Write the source code and init file from templates for the custom component.
|
||||
|
||||
Args:
|
||||
custom_component_src_dir: The name of the custom component source directory.
|
||||
component_class_name: The name of the component class.
|
||||
module_name: The name of the module.
|
||||
"""
|
||||
from reflex.compiler import templates
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
custom_component_src_dir,
|
||||
f"{module_name}.py",
|
||||
),
|
||||
"w",
|
||||
) as f:
|
||||
f.write(
|
||||
templates.CUSTOM_COMPONENTS_SOURCE.render(
|
||||
component_class_name=component_class_name, module_name=module_name
|
||||
)
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
custom_component_src_dir,
|
||||
CustomComponents.INIT_FILE,
|
||||
),
|
||||
"w",
|
||||
) as f:
|
||||
f.write(templates.CUSTOM_COMPONENTS_INIT_FILE.render(module_name=module_name))
|
||||
|
||||
|
||||
def _populate_demo_app(name_variants: NameVariants):
|
||||
"""Populate the demo app that imports the custom components.
|
||||
|
||||
Args:
|
||||
name_variants: the tuple including various names such as package name, class name needed for the project.
|
||||
"""
|
||||
from reflex import constants
|
||||
from reflex.compiler import templates
|
||||
from reflex.reflex import _init
|
||||
|
||||
demo_app_dir = name_variants.demo_app_dir
|
||||
demo_app_name = name_variants.demo_app_name
|
||||
|
||||
console.info(f"Creating app for testing: {demo_app_dir}")
|
||||
|
||||
os.makedirs(demo_app_dir)
|
||||
|
||||
with set_directory(demo_app_dir):
|
||||
# We start with the blank template as basis.
|
||||
_init(name=demo_app_name, template=constants.Templates.Kind.BLANK)
|
||||
# Then overwrite the app source file with the one we want for testing custom components.
|
||||
# This source file is rendered using jinja template file.
|
||||
with open(f"{demo_app_name}/{demo_app_name}.py", "w") as f:
|
||||
f.write(
|
||||
templates.CUSTOM_COMPONENTS_DEMO_APP.render(
|
||||
custom_component_module_dir=name_variants.custom_component_module_dir,
|
||||
module_name=name_variants.module_name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _get_default_library_name_parts() -> list[str]:
|
||||
"""Get the default library name. Based on the current directory name, remove any non-alphanumeric characters.
|
||||
|
||||
Raises:
|
||||
ValueError: If the current directory name is not suitable for python projects, and we cannot find a valid library name based off it.
|
||||
|
||||
Returns:
|
||||
The parts of default library name.
|
||||
"""
|
||||
current_dir_name = os.getcwd().split(os.path.sep)[-1]
|
||||
|
||||
cleaned_dir_name = re.sub("[^0-9a-zA-Z-_]+", "", current_dir_name)
|
||||
parts = re.split("-|_", cleaned_dir_name)
|
||||
if not parts:
|
||||
# The folder likely has a name not suitable for python paths.
|
||||
raise ValueError(
|
||||
f"Could not find a valid library name based on the current directory: got {current_dir_name}."
|
||||
)
|
||||
return parts
|
||||
|
||||
|
||||
NameVariants = namedtuple(
|
||||
"NameVariants",
|
||||
[
|
||||
"library_name",
|
||||
"component_class_name",
|
||||
"package_name",
|
||||
"module_name",
|
||||
"custom_component_module_dir",
|
||||
"demo_app_dir",
|
||||
"demo_app_name",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def _validate_library_name(library_name: str | None) -> NameVariants:
|
||||
"""Validate the library name.
|
||||
|
||||
Args:
|
||||
library_name: The name of the library if picked otherwise None.
|
||||
|
||||
Raises:
|
||||
Exit: If the library name is not suitable for python projects.
|
||||
|
||||
Returns:
|
||||
A tuple containing the various names such as package name, class name, etc., needed for the project.
|
||||
"""
|
||||
if library_name is not None and not re.match(
|
||||
r"^[a-zA-Z-]+[a-zA-Z0-9-]*$", library_name
|
||||
):
|
||||
console.error(
|
||||
f"Please use only alphanumeric characters or dashes: got {library_name}"
|
||||
)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
# If not specified, use the current directory name to form the module name.
|
||||
name_parts = (
|
||||
[part.lower() for part in library_name.split("-")]
|
||||
if library_name
|
||||
else _get_default_library_name_parts()
|
||||
)
|
||||
if not library_name:
|
||||
library_name = "-".join(name_parts)
|
||||
|
||||
# Component class name is the camel case.
|
||||
component_class_name = "".join([part.capitalize() for part in name_parts])
|
||||
console.info(f"Component class name: {component_class_name}")
|
||||
|
||||
# Package name is commonly kebab case.
|
||||
package_name = f"reflex-{library_name}"
|
||||
console.info(f"Package name: {package_name}")
|
||||
|
||||
# Module name is the snake case.
|
||||
module_name = "_".join(name_parts)
|
||||
|
||||
custom_component_module_dir = f"reflex_{module_name}"
|
||||
console.info(f"Custom component source directory: {custom_component_module_dir}")
|
||||
|
||||
# Use the same name for the directory and the app.
|
||||
demo_app_dir = demo_app_name = f"{module_name}_demo"
|
||||
console.info(f"Demo app directory: {demo_app_dir}")
|
||||
|
||||
return NameVariants(
|
||||
library_name=library_name,
|
||||
component_class_name=component_class_name,
|
||||
package_name=package_name,
|
||||
module_name=module_name,
|
||||
custom_component_module_dir=custom_component_module_dir,
|
||||
demo_app_dir=demo_app_dir,
|
||||
demo_app_name=demo_app_name,
|
||||
)
|
||||
|
||||
|
||||
def _populate_custom_component_project(name_variants: NameVariants):
|
||||
"""Populate the custom component source directory. This includes the pyproject.toml, README.md, and the code template for the custom component.
|
||||
|
||||
Args:
|
||||
name_variants: the tuple including various names such as package name, class name needed for the project.
|
||||
"""
|
||||
console.info(
|
||||
f"Populating pyproject.toml with package name: {name_variants.package_name}"
|
||||
)
|
||||
# write pyproject.toml, README.md, etc.
|
||||
_create_package_config(
|
||||
module_name=name_variants.library_name, package_name=name_variants.package_name
|
||||
)
|
||||
_create_readme(
|
||||
module_name=name_variants.library_name, package_name=name_variants.package_name
|
||||
)
|
||||
|
||||
console.info(
|
||||
f"Initializing the component directory: {CustomComponents.SRC_DIR}/{name_variants.custom_component_module_dir}"
|
||||
)
|
||||
os.makedirs(CustomComponents.SRC_DIR)
|
||||
with set_directory(CustomComponents.SRC_DIR):
|
||||
os.makedirs(name_variants.custom_component_module_dir)
|
||||
_write_source_and_init_py(
|
||||
custom_component_src_dir=name_variants.custom_component_module_dir,
|
||||
component_class_name=name_variants.component_class_name,
|
||||
module_name=name_variants.module_name,
|
||||
)
|
||||
|
||||
|
||||
@custom_components_cli.command(name="init")
|
||||
def init(
|
||||
library_name: Optional[str] = typer.Option(
|
||||
None,
|
||||
help="The name of your library. On PyPI, package will be published as `reflex-{library-name}`.",
|
||||
),
|
||||
install: bool = typer.Option(
|
||||
True,
|
||||
help="Whether to install package from this local custom component in editable mode.",
|
||||
),
|
||||
loglevel: constants.LogLevel = typer.Option(
|
||||
config.loglevel, help="The log level to use."
|
||||
),
|
||||
):
|
||||
"""Initialize a custom component.
|
||||
|
||||
Args:
|
||||
library_name: The name of the library.
|
||||
install: Whether to install package from this local custom component in editable mode.
|
||||
loglevel: The log level to use.
|
||||
|
||||
Raises:
|
||||
Exit: If the pyproject.toml already exists.
|
||||
"""
|
||||
from reflex.utils import exec, prerequisites
|
||||
|
||||
console.set_log_level(loglevel)
|
||||
|
||||
if os.path.exists(CustomComponents.PYPROJECT_TOML):
|
||||
console.error(f"A {CustomComponents.PYPROJECT_TOML} already exists. Aborting.")
|
||||
typer.Exit(code=1)
|
||||
|
||||
# Show system info.
|
||||
exec.output_system_info()
|
||||
|
||||
# Check the name follows the convention if picked.
|
||||
name_variants = _validate_library_name(library_name)
|
||||
|
||||
_populate_custom_component_project(name_variants)
|
||||
|
||||
_populate_demo_app(name_variants)
|
||||
|
||||
# Initialize the .gitignore.
|
||||
prerequisites.initialize_gitignore()
|
||||
|
||||
if install:
|
||||
package_name = name_variants.package_name
|
||||
console.info(f"Installing {package_name} in editable mode.")
|
||||
if _pip_install_on_demand(package_name=".", install_args=["-e"]):
|
||||
console.info(f"Package {package_name} installed!")
|
||||
else:
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
console.print("Custom component initialized successfully!")
|
||||
console.print("Here's the summary:")
|
||||
console.print(
|
||||
f"{CustomComponents.PYPROJECT_TOML} and {CustomComponents.PACKAGE_README} created. [bold]Please fill in details such as your name, email, homepage URL.[/bold]"
|
||||
)
|
||||
console.print(
|
||||
f"Source code template is in {CustomComponents.SRC_DIR}. [bold]Start by editing it with your component implementation.[/bold]"
|
||||
)
|
||||
console.print(
|
||||
f"Demo app created in {name_variants.demo_app_dir}. [bold]Use this app to test your custom component.[/bold]"
|
||||
)
|
||||
|
||||
|
||||
def _pip_install_on_demand(
|
||||
package_name: str,
|
||||
install_args: list[str] | None = None,
|
||||
) -> bool:
|
||||
"""Install a package on demand.
|
||||
|
||||
Args:
|
||||
package_name: The name of the package.
|
||||
install_args: The additional arguments for the pip install command.
|
||||
|
||||
Returns:
|
||||
True if the package is installed successfully, False otherwise.
|
||||
"""
|
||||
install_args = install_args or []
|
||||
|
||||
install_cmds = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
*install_args,
|
||||
package_name,
|
||||
]
|
||||
console.debug(f"Install package: {' '.join(install_cmds)}")
|
||||
return _run_commands_in_subprocess(install_cmds)
|
||||
|
||||
|
||||
def _run_commands_in_subprocess(cmds: list[str]) -> bool:
|
||||
"""Run commands in a subprocess.
|
||||
|
||||
Args:
|
||||
cmds: The commands to run.
|
||||
|
||||
Returns:
|
||||
True if the command runs successfully, False otherwise.
|
||||
"""
|
||||
console.debug(f"Running command: {' '.join(cmds)}")
|
||||
try:
|
||||
result = subprocess.run(cmds, capture_output=True, text=True, check=True)
|
||||
console.debug(result.stdout)
|
||||
return True
|
||||
except subprocess.CalledProcessError as cpe:
|
||||
console.error(cpe.stdout)
|
||||
console.error(cpe.stderr)
|
||||
return False
|
||||
|
||||
|
||||
@custom_components_cli.command(name="build")
|
||||
def build(
|
||||
loglevel: constants.LogLevel = typer.Option(
|
||||
config.loglevel, help="The log level to use."
|
||||
),
|
||||
):
|
||||
"""Build a custom component. Must be run from the project root directory where the pyproject.toml is.
|
||||
|
||||
Args:
|
||||
loglevel: The log level to use.
|
||||
|
||||
Raises:
|
||||
Exit: If the build fails.
|
||||
"""
|
||||
console.set_log_level(loglevel)
|
||||
console.print("Building custom component...")
|
||||
|
||||
cmds = [sys.executable, "-m", "build", "."]
|
||||
if _run_commands_in_subprocess(cmds):
|
||||
console.info("Custom component built successfully!")
|
||||
else:
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
|
||||
def _validate_repository_name(repository: str | None) -> str:
|
||||
"""Validate the repository name.
|
||||
|
||||
Args:
|
||||
repository: The name of the repository.
|
||||
|
||||
Returns:
|
||||
The name of the repository.
|
||||
|
||||
Raises:
|
||||
Exit: If the repository name is not supported.
|
||||
"""
|
||||
if repository is None:
|
||||
return "pypi"
|
||||
elif repository not in CustomComponents.REPO_URLS:
|
||||
console.error(
|
||||
f"Unsupported repository name. Allow {CustomComponents.REPO_URLS.keys()}, got {repository}"
|
||||
)
|
||||
raise typer.Exit(code=1)
|
||||
return repository
|
||||
|
||||
|
||||
def _validate_credentials(
|
||||
username: str | None, password: str | None, token: str | None
|
||||
) -> tuple[str, str]:
|
||||
"""Validate the credentials.
|
||||
|
||||
Args:
|
||||
username: The username to use for authentication on python package repository.
|
||||
password: The password to use for authentication on python package repository.
|
||||
token: The token to use for authentication on python package repository.
|
||||
|
||||
Raises:
|
||||
Exit: If the appropriate combination of credentials is not provided.
|
||||
|
||||
Returns:
|
||||
The username and password.
|
||||
"""
|
||||
if token is not None:
|
||||
if username is not None or password is not None:
|
||||
console.error("Cannot use token and username/password at the same time.")
|
||||
raise typer.Exit(code=1)
|
||||
username = "__token__"
|
||||
password = token
|
||||
elif username is None or password is None:
|
||||
console.error(
|
||||
"Must provide both username and password for authentication if not using a token."
|
||||
)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
return username, password
|
||||
|
||||
|
||||
def _ensure_dist_dir():
|
||||
"""Ensure the distribution directory and the expected files exist.
|
||||
|
||||
Raises:
|
||||
Exit: If the distribution directory does not exist or the expected files are not found.
|
||||
"""
|
||||
dist_dir = Path(CustomComponents.DIST_DIR)
|
||||
|
||||
# Check if the distribution directory exists.
|
||||
if not dist_dir.exists():
|
||||
console.error(f"Directory {dist_dir.name} does not exist. Please build first.")
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
# Check if the distribution directory is indeed a directory.
|
||||
if not dist_dir.is_dir():
|
||||
console.error(
|
||||
f"{dist_dir.name} is not a directory. If this is a file you added, move it and rebuild."
|
||||
)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
# Check if the distribution files exist.
|
||||
for suffix in CustomComponents.DISTRIBUTION_FILE_SUFFIXES:
|
||||
if not list(dist_dir.glob(f"*{suffix}")):
|
||||
console.error(
|
||||
f"Expected distribution file with suffix {suffix} in directory {dist_dir.name}"
|
||||
)
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
|
||||
@custom_components_cli.command(name="publish")
|
||||
def publish(
|
||||
repository: Optional[str] = typer.Option(
|
||||
None,
|
||||
"-r",
|
||||
"--repository",
|
||||
help="The name of the repository. Defaults to pypi. Only supports pypi and testpypi (Test PyPI) for now.",
|
||||
),
|
||||
token: Optional[str] = typer.Option(
|
||||
None,
|
||||
"-t",
|
||||
"--token",
|
||||
help="The API token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time",
|
||||
),
|
||||
username: Optional[str] = typer.Option(
|
||||
None,
|
||||
"-u",
|
||||
"--username",
|
||||
help="The username to use for authentication on python package repository. Username and password must both be provided.",
|
||||
),
|
||||
password: Optional[str] = typer.Option(
|
||||
None,
|
||||
"-p",
|
||||
"--password",
|
||||
help="The password to use for authentication on python package repository. Username and password must both be provided.",
|
||||
),
|
||||
loglevel: constants.LogLevel = typer.Option(
|
||||
config.loglevel, help="The log level to use."
|
||||
),
|
||||
):
|
||||
"""Publish a custom component. Must be run from the project root directory where the pyproject.toml is.
|
||||
|
||||
Args:
|
||||
repository: The name of the Python package repository, such pypi, testpypi.
|
||||
token: The token to use for authentication on python package repository. If token is provided, no username/password should be provided at the same time.
|
||||
username: The username to use for authentication on python package repository.
|
||||
password: The password to use for authentication on python package repository.
|
||||
loglevel: The log level to use.
|
||||
|
||||
Raises:
|
||||
Exit: If arguments provided are not correct or the publish fails.
|
||||
"""
|
||||
console.set_log_level(loglevel)
|
||||
|
||||
# Validate the repository name.
|
||||
repository = _validate_repository_name(repository)
|
||||
console.print(f"Publishing custom component to {repository}...")
|
||||
|
||||
# Validate the credentials.
|
||||
username, password = _validate_credentials(username, password, token)
|
||||
|
||||
# Validate the distribution directory.
|
||||
_ensure_dist_dir()
|
||||
|
||||
# We install twine on the fly if required so it is not a stable dependency of reflex.
|
||||
try:
|
||||
import twine # noqa: F401 # type: ignore
|
||||
except (ImportError, ModuleNotFoundError) as ex:
|
||||
if not _pip_install_on_demand("twine"):
|
||||
raise typer.Exit(code=1) from ex
|
||||
publish_cmds = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"twine",
|
||||
"upload",
|
||||
"--repository-url",
|
||||
CustomComponents.REPO_URLS[repository],
|
||||
"--username",
|
||||
username,
|
||||
"--password",
|
||||
password,
|
||||
"--non-interactive",
|
||||
f"{CustomComponents.DIST_DIR}/*",
|
||||
]
|
||||
if _run_commands_in_subprocess(publish_cmds):
|
||||
console.info("Custom component published successfully!")
|
||||
else:
|
||||
raise typer.Exit(1)
|
@ -15,6 +15,7 @@ from reflex_cli.utils import dependency
|
||||
|
||||
from reflex import constants
|
||||
from reflex.config import get_config
|
||||
from reflex.custom_components.custom_components import custom_components_cli
|
||||
from reflex.utils import console, telemetry
|
||||
|
||||
# Disable typer+rich integration for help panels
|
||||
@ -580,6 +581,11 @@ cli.add_typer(
|
||||
name="deployments",
|
||||
help="Subcommands for managing the Deployments.",
|
||||
)
|
||||
cli.add_typer(
|
||||
custom_components_cli,
|
||||
name="component",
|
||||
help="Subcommands for creating and publishing Custom Components.",
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
|
Loading…
Reference in New Issue
Block a user