diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index f1ce1159..6623fd9b 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -1,39 +1,30 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - name: Upload Python Package on: release: - types: [published] + types: + - published permissions: contents: read jobs: - deploy: - + build-and-publish: runs-on: ubuntu-latest - + permissions: + id-token: write steps: - - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: '3.x' + python-version: "3.12" - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + run: python -m pip install -U setuptools wheel build + - name: Build + run: python -m build . + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + skip-existing: true diff --git a/README.md b/README.md index d203c22f..32547a92 100644 --- a/README.md +++ b/README.md @@ -60,18 +60,13 @@ Note that LEADS requires **Python >= 3.12**. To set up the environment on a Rasp command, see [Environment Setup](#environment-setup). ```shell -pip install Pillow PySDL2 customtkinter gpiozero lgpio opencv-python-headless pynmea2 pynput pysdl2-dll pyserial screeninfo leads +pip install "leads[standard]" ``` -`numpy` and `pandas` will be automatically installed with `leads`. - -`Pillow`, `PySDL2`, `customtkinter`, `gpiozero`, `lgpio`, `opencv-python-headless`, `pynmea2`, `pynput`, `pysdl2-dll`, -`pyserial`, and `screeninfo` are optional. - -If your platform does not support GPIO, just exclude `lgpio`. +If your platform does not support GPIO, use profile "no-gpio. ```shell -pip install Pillow PySDL2 customtkinter gpiozero opencv-python-headless pynmea2 pynput pysdl2-dll pyserial screeninfo leads +pip install "leads[no-gpio]" ``` If you only want the framework, run the following. @@ -83,7 +78,7 @@ pip install leads #### Verify ```shell -python -m leads_vec info +leads-vec info ``` ### Arduino @@ -101,7 +96,7 @@ the framework in your project. ### Main ```shell -python -m leads_vec run +leads-vec run ``` #### Optional Arguments @@ -109,13 +104,13 @@ python -m leads_vec run Run the following to get a list of all the supported arguments. ```shell -python -m leads_vec -h +leads-vec -h ``` ##### Specify a Configuration File ```shell -python -m leads_vec -c path/to/the/config/file.json run +leads-vec -c path/to/the/config/file.json run ``` > You can use ":INTERNAL" to replace the path to `leads_vec`. For example, instead of typing @@ -128,7 +123,7 @@ To learn about the configuration file, read [Configurations](#Configurations). ##### Specify a Devices Module ```shell -python -m leads_vec -d path/to/the/devices.py run +leads-vec -d path/to/the/devices.py run ``` > You can use ":INTERNAL" to replace the path to `leads_vec`. For example, instead of typing @@ -139,7 +134,7 @@ To learn about the devices module, read [Devices Module](#devices-module). ##### Specify a Main Module ```shell -python -m leads_vec -m path/to/the/main.py run +leads-vec -m path/to/the/main.py run ``` > You can use ":INTERNAL" to replace the path to `leads_vec`. For example, instead of typing @@ -160,7 +155,7 @@ def main() -> int: ##### Generate a Configuration File ```shell -python -m leads_vec -r config run +leads-vec -r config run ``` This will generate a default "config.json" file under the current directory. @@ -168,7 +163,7 @@ This will generate a default "config.json" file under the current directory. ##### Register a Systemd Service ```shell -python -m leads_vec -r systemd run +leads-vec -r systemd run ``` This will register a user Systemd service to start the program. @@ -195,7 +190,7 @@ systemctl --user disable leads-vec ##### Use Reverse Proxy ```shell -python -m leads_vec -r reverse_proxy run +leads-vec -r reverse_proxy run ``` This will start the corresponding reverse proxy program as a subprocess in the background. @@ -203,7 +198,7 @@ This will start the corresponding reverse proxy program as a subprocess in the b ##### Specify a Theme ```shell -python -m leads_vec -t path/to/the/theme.json run +leads-vec -t path/to/the/theme.json run ``` > You can use ":INTERNAL" to replace the path to `leads_vec`. For example, instead of typing @@ -214,7 +209,7 @@ To learn about themes, read [Color and Themes](https://customtkinter.tomschimans ##### Magnify Font Sizes ```shell -python -m leads_vec -mfs 1.5 run +leads-vec -mfs 1.5 run ``` This will magnify all font sizes by 1.5. @@ -222,7 +217,7 @@ This will magnify all font sizes by 1.5. ##### Use Emulation ```shell -python -m leads_vec --emu run +leads-vec --emu run ``` This will force the program to use emulation even if the environment is available. @@ -230,7 +225,7 @@ This will force the program to use emulation even if the environment is availabl ##### Automatically Magnify Font Sizes ```shell -python -m leads_vec --auto-mfs run +leads-vec --auto-mfs run ``` Similar to [Magnify Font Sizes](#magnify-font-sizes), but instead of manually deciding the factor, the program will @@ -238,8 +233,14 @@ automatically calculate the best factor to keep the original proportion as desig ### Remote Analyst +The remote analyst requires additional dependencies. Install them through the following command. + +```shell +pip install "leads[all]" +``` + ```shell -python -m leads_vec_rc +leads-vec-rc ``` Go to the online dashboard at https://leads-vec-rc.projectneura.org. @@ -249,13 +250,13 @@ Go to the online dashboard at https://leads-vec-rc.projectneura.org. Run the following to get a list of all the supported arguments. ```shell -python -m leads_vec_rc -h +leads-vec-rc -h ``` ##### Server Port ```shell -python -m leads_vec_rc -p 80 +leads-vec-rc -p 80 ``` If not specified, the port is `8000` by default. @@ -263,7 +264,7 @@ If not specified, the port is `8000` by default. ##### Specify a Configuration File ```shell -python -m leads_vec_rc -c path/to/the/config/file.json +leads-vec-rc -c path/to/the/config/file.json ``` If not specified, all configurations will be default values. diff --git a/leads_vec/__entry__.py b/leads_vec/__entry__.py new file mode 100644 index 00000000..76c0b914 --- /dev/null +++ b/leads_vec/__entry__.py @@ -0,0 +1,64 @@ +from argparse import ArgumentParser as _ArgumentParser, BooleanOptionalAction as _BooleanOptionalAction +from importlib.metadata import version as _package_version, PackageNotFoundError as _PackageNotFoundError +from os import getlogin as _get_login +from os.path import abspath as _abspath +from sys import exit as _exit, version as _version +from warnings import filterwarnings as _filterwarnings + +from leads import L as _L +from leads_gui.system import get_system_kernel as _get_system_kernel +from leads_vec.run import run + +MODULE_PATH = _abspath(__file__)[:-13] + + +def parse_path(path: str | None) -> str | None: + return path.replace(":INTERNAL", MODULE_PATH) if path else None + + +def __entry__() -> None: + _filterwarnings("ignore") + + parser = _ArgumentParser(prog="LEADS VeC", description="Lightweight Embedded Assisted Driving System VeC", + epilog="Project Neura: https://projectneura.org\n" + "GitHub: https://github.com/ProjectNeura/LEADS") + parser.add_argument("action", choices=("info", "replay", "run")) + parser.add_argument("-c", "--config", default=None, help="specify a configuration file") + parser.add_argument("-d", "--devices", default=f"{MODULE_PATH}/devices.py", help="specify a devices module") + parser.add_argument("-m", "--main", default=f"{MODULE_PATH}/cli.py", help="specify a main module") + parser.add_argument("-r", "--register", choices=("systemd", "config", "reverse_proxy"), default=None, + help="register a service") + parser.add_argument("-t", "--theme", default=None, help="specify a theme") + parser.add_argument("-mfs", "--magnify-font-sizes", type=float, default=1, help="magnify font sizes by a factor") + parser.add_argument("--emu", action=_BooleanOptionalAction, default=False, help="use emulator") + parser.add_argument("--auto-mfs", action=_BooleanOptionalAction, default=False, + help="automatically magnify font sizes to match the original proportion") + parser.add_argument("--ignore-import-error", action=_BooleanOptionalAction, default=False, + help="ignore `ImportError`") + args = parser.parse_args() + if args.action == "info": + from leads_vec.__version__ import __version__ + from ._bootloader import frpc_exists as _frpc_exists + + leads_version = "Unknown" + try: + leads_version = _package_version("leads") + except _PackageNotFoundError: + _L.warn("Failed to retrieve package version (did you install through pip?)") + _L.info(f"LEADS VeC", + f"System Kernel: {_get_system_kernel().upper()}", + f"Python Version: {_version}", + f"User: {_get_login()}", + f"`frpc` Available: {_frpc_exists()}", + f"Module Path: {MODULE_PATH}", + f"LEADS Version: {leads_version}", + f"LEADS VeC Version: {__version__}", + sep="\n") + else: + if args.action == "replay": + args.devices = f"{MODULE_PATH}/replay.py" + args.emu = False + _L.debug("Replay mode enabled") + _exit(run(parse_path(args.config), parse_path(args.devices), parse_path(args.main), args.register, + parse_path(args.theme), args.magnify_font_sizes, args.emu, args.auto_mfs, args.ignore_import_error)) + _exit() diff --git a/leads_vec/__init__.py b/leads_vec/__init__.py index 902f570d..12ba1208 100644 --- a/leads_vec/__init__.py +++ b/leads_vec/__init__.py @@ -4,3 +4,4 @@ raise ImportError("Please install `pynput` to run this module\n>>>pip install pynput") from leads_vec.run import * +from leads_vec.__entry__ import __entry__ diff --git a/leads_vec/__main__.py b/leads_vec/__main__.py index 2156dcbc..8b736c31 100644 --- a/leads_vec/__main__.py +++ b/leads_vec/__main__.py @@ -1,63 +1,5 @@ -from argparse import ArgumentParser as _ArgumentParser, BooleanOptionalAction as _BooleanOptionalAction -from importlib.metadata import version as _package_version, PackageNotFoundError as _PackageNotFoundError -from os import getlogin as _get_login -from os.path import abspath as _abspath -from sys import exit as _exit, version as _version -from warnings import filterwarnings as _filterwarnings - -from leads import L as _L -from leads_gui.system import get_system_kernel as _get_system_kernel -from leads_vec.run import run - - -def parse_path(path: str | None) -> str | None: - return path.replace(":INTERNAL", MODULE_PATH) if path else None +from leads_vec.__entry__ import __entry__ if __name__ == "__main__": - _filterwarnings("ignore") - MODULE_PATH = _abspath(__file__)[:-12] - - parser = _ArgumentParser(prog="LEADS VeC", description="Lightweight Embedded Assisted Driving System VeC", - epilog="ProjectNeura: https://projectneura.org\n" - "GitHub: https://github.com/ProjectNeura/LEADS") - parser.add_argument("action", choices=("info", "replay", "run")) - parser.add_argument("-c", "--config", default=None, help="specify a configuration file") - parser.add_argument("-d", "--devices", default=f"{MODULE_PATH}/devices.py", help="specify a devices module") - parser.add_argument("-m", "--main", default=f"{MODULE_PATH}/cli.py", help="specify a main module") - parser.add_argument("-r", "--register", choices=("systemd", "config", "reverse_proxy"), default=None, - help="register a service") - parser.add_argument("-t", "--theme", default=None, help="specify a theme") - parser.add_argument("-mfs", "--magnify-font-sizes", type=float, default=1, help="magnify font sizes by a factor") - parser.add_argument("--emu", action=_BooleanOptionalAction, default=False, help="use emulator") - parser.add_argument("--auto-mfs", action=_BooleanOptionalAction, default=False, - help="automatically magnify font sizes to match the original proportion") - parser.add_argument("--ignore-import-error", action=_BooleanOptionalAction, default=False, - help="ignore `ImportError`") - args = parser.parse_args() - if args.action == "info": - from leads_vec.__version__ import __version__ - from ._bootloader import frpc_exists as _frpc_exists - - leads_version = "Unknown" - try: - leads_version = _package_version("leads") - except _PackageNotFoundError: - _L.warn("Failed to retrieve package version (did you install through pip?)") - _L.info(f"LEADS VeC", - f"System Kernel: {_get_system_kernel().upper()}", - f"Python Version: {_version}", - f"User: {_get_login()}", - f"`frpc` Available: {_frpc_exists()}", - f"Module Path: {MODULE_PATH}", - f"LEADS Version: {leads_version}", - f"LEADS VeC Version: {__version__}", - sep="\n") - else: - if args.action == "replay": - args.devices = f"{MODULE_PATH}/replay.py" - args.emu = False - _L.debug("Replay mode enabled") - _exit(run(parse_path(args.config), parse_path(args.devices), parse_path(args.main), args.register, - parse_path(args.theme), args.magnify_font_sizes, args.emu, args.auto_mfs, args.ignore_import_error)) - _exit() + __entry__() diff --git a/leads_vec_rc/__entry__.py b/leads_vec_rc/__entry__.py new file mode 100644 index 00000000..24affceb --- /dev/null +++ b/leads_vec_rc/__entry__.py @@ -0,0 +1,19 @@ +from argparse import ArgumentParser as _ArgumentParser + +from uvicorn import run as _run + +from leads import register_config as _register_config, load_config as _load_config +from leads_gui import Config as _Config + + +def __entry__() -> None: + parser = _ArgumentParser(prog="LEADS VeC RC", + description="Lightweight Embedded Assisted Driving System VeC Remote Controller", + epilog="GitHub: https://github.com/ProjectNeura/LEADS") + parser.add_argument("-c", "--config", default=None, help="specify a configuration file") + parser.add_argument("-p", "--port", type=int, default=8000, help="specify a server port") + args = parser.parse_args() + _register_config(_load_config(args.config, _Config) if args.config else _Config({})) + from leads_vec_rc.cli import app + + _run(app, host="0.0.0.0", port=args.port, log_level="warning") \ No newline at end of file diff --git a/leads_vec_rc/__init__.py b/leads_vec_rc/__init__.py index 9a76ae93..34f976e9 100644 --- a/leads_vec_rc/__init__.py +++ b/leads_vec_rc/__init__.py @@ -1,6 +1,8 @@ from importlib.util import find_spec as _find_spec if not _find_spec("fastapi"): - raise ImportError("Please install `fastapi` to run this module\n>>>pip install \"fastapi[all]\"") + raise ImportError("Please install `fastapi` to run this module\n>>>pip install \"fastapi[standard]\"") if not _find_spec("uvicorn"): - raise ImportError("Please install `uvicorn` to run this module\n>>>pip install \"fastapi[all]\"") + raise ImportError("Please install `uvicorn` to run this module\n>>>pip install \"fastapi[standard]\"") + +from leads_vec_rc.__entry__ import __entry__ diff --git a/leads_vec_rc/__main__.py b/leads_vec_rc/__main__.py index db82cce0..c290287a 100644 --- a/leads_vec_rc/__main__.py +++ b/leads_vec_rc/__main__.py @@ -1,18 +1,4 @@ -from argparse import ArgumentParser as _ArgumentParser - -from uvicorn import run as _run - -from leads import register_config as _register_config, load_config as _load_config -from leads_gui import Config as _Config +from leads_vec_rc.__entry__ import __entry__ if __name__ == "__main__": - parser = _ArgumentParser(prog="LEADS VeC RC", - description="Lightweight Embedded Assisted Driving System VeC Remote Controller", - epilog="GitHub: https://github.com/ProjectNeura/LEADS") - parser.add_argument("-c", "--config", default=None, help="specify a configuration file") - parser.add_argument("-p", "--port", type=int, default=8000, help="specify a server port") - args = parser.parse_args() - _register_config(_load_config(args.config, _Config) if args.config else _Config({})) - from leads_vec_rc.cli import app - - _run(app, host="0.0.0.0", port=args.port, log_level="warning") + __entry__() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..29de3a76 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "leads" +version = "0.9.2-alpha.2" +description = "Lightweight Embedded Assisted Driving System" +license = "Apache-2.0" +readme = "README.md" +requires-python = ">=3.12" +authors = [ + { name = "Project Neura", email = "central@projectneura.org" } +] +dependencies = ["numpy", "pandas"] + +[project.optional-dependencies] +standard = [ + "Pillow", "PySDL2", "customtkinter", "gpiozero", "lgpio", "opencv-python-headless", "pynmea2", "pynput", + "pysdl2-dll", "pyserial", "screeninfo" +] +no-gpio = [ + "Pillow", "PySDL2", "customtkinter", "gpiozero", "opencv-python-headless", "pynmea2", "pynput", "pysdl2-dll", + "pyserial", "screeninfo" +] +all = [ + "Pillow", "PySDL2", "customtkinter", "fastapi[standard]", "gpiozero", "lgpio", "opencv-python-headless", "pynmea2", + "pynput", "pysdl2-dll", "pyserial", "screeninfo" +] + +[tool.hatch.build.targets.sdist] +only-include = ["leads", "leads_arduino", "leads_audio", "leads_can", "leads_comm_serial", "leads_emulation", + "leads_gpio", "leads_gui", "leads_video", "leads_vec", "leads_vec_rc", "design", "docs"] + +[tool.hatch.build.targets.wheel] +packages = ["leads", "leads_arduino", "leads_audio", "leads_can", "leads_comm_serial", "leads_emulation", "leads_gpio", + "leads_gui", "leads_video", "leads_vec", "leads_vec_rc"] + +[project.urls] +Homepage = "https://leads.projectneura.org" +Documentation = "https://leads-docs.projectneura.org" +Repository = "https://github.com/ProjectNeura/LEADS" + +[project.scripts] +leads-vec-rc = "leads_vec_rc:__entry__" + +[project.gui-scripts] +leads-vec = "leads_vec:__entry__" diff --git a/scripts/setup.sh b/scripts/setup.sh index 161776b8..e9026815 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -21,10 +21,6 @@ execute_root "/bin/bash" "python-install.sh" echo "Cleaning up..." execute_root "rm" "python-install.sh" echo "Installing dependencies..." -execute_root "pip-leads" "install" "Pillow" "PySDL2" "customtkinter" "gpiozero" "lgpio" "opencv-python-headless" "pynmea2" "pynput" "pysdl2-dll" "pyserial" "screeninfo" "leads" -echo "Creating Shortcut..." -execute_root "echo" "#!/bin/bash" > "/bin/leads-vec" -execute_root "echo" 'python-leads -m leads_vec "$@"' >> "/bin/leads-vec" -execute_root "chmod" "+x" "/bin/leads-vec" +execute_root "pip-leads" "install" '"leads[standard]"' echo "Verifying..." execute "leads-vec" "info" \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index a5cb98d2..00000000 --- a/setup.py +++ /dev/null @@ -1,25 +0,0 @@ -from setuptools import find_packages, setup - -with open("README.md") as f: - LONG_DESCRIPTION = f.read() - -setup( - name="leads", - version="0.9.2-alpha.2", - python_requires=">=3.12", - author="ProjectNeura", - author_email="central@projectneura.org", - description="Lightweight Embedded Assisted Driving System", - license="Apache License 2.0", - long_description=LONG_DESCRIPTION, - long_description_content_type="text/markdown", - url="https://github.com/ProjectNeura/LEADS", - packages=find_packages(), - package_data={ - "leads_audio": ["assets/*"], - "leads_gui": ["assets/*", "assets/icons/*"], - "leads_vec": ["_bootloader/leads-vec.service.sh"] - }, - include_package_data=True, - install_requires=["numpy", "pandas"] -)