From 38a86b378ff8c06010d74620887f3aca2f3d3958 Mon Sep 17 00:00:00 2001 From: Antoine Romero-Romero <45259848+AntoineRR@users.noreply.github.com> Date: Wed, 20 Jul 2022 21:43:28 +0100 Subject: [PATCH 1/7] chore(ci): move pytest from CircleCi to Github Actions (#241) --- .circleci/config.yml | 157 --------------------- .github/workflows/{CI.yml => build-CI.yml} | 4 +- .github/workflows/python-CI.yml | 33 +++++ .github/workflows/rust-CI.yml | 2 + robyn/test-requirements.txt | 2 +- 5 files changed, 39 insertions(+), 159 deletions(-) delete mode 100644 .circleci/config.yml rename .github/workflows/{CI.yml => build-CI.yml} (98%) create mode 100644 .github/workflows/python-CI.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index dcadffa21..000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,157 +0,0 @@ -version: 2.1 - -orbs: - # The python orb contains a set of prepackaged CircleCI configuration you can use repeatedly in your configuration files - # Orb commands and jobs help you with common scripting around a language/tool - # so you dont have to copy and paste it everywhere. - # See the orb documentation here: https://circleci.com/developer/orbs/orb/circleci/python - python: circleci/python@1.2 - rust: circleci/rust@1.5.0 - -commands: - test: - description: | - Tests for rust sub section of robyn - parameters: - cache_version: - default: v1 - description: Cache version to use - increment this to build a fresh cache. - type: string - package: - default: "" - description: Package to test. - type: string - with_cache: - default: true - description: Whether to restore and save the cache or not - set to no if running multiple commands in one job. - type: boolean - working_directory: - default: ~/project - description: Path to the directory containing your Cargo.lock file. Not needed if Cargo.lock lives in the root. - type: string - steps: - - when: - condition: <> - steps: - - restore_cache: - keys: - - cargo-<>-{{ checksum "Cargo.lock" }} - - run: - # command: cargo test <> - command: cargo test --no-default-features - working_directory: <> - - when: - condition: <> - steps: - - save_cache: - key: cargo-<>-{{ checksum "Cargo.lock" }} - paths: - - ~/.cargo - -workflows: - e2e-testing: # This is the name of the workflow, feel free to change it to better match your workflow. - # Inside the workflow, you define the jobs you want to run. - # For more details on extending your workflow, see the configuration docs: https://circleci.com/docs/2.0/configuration-reference/#workflows - jobs: - # - rust/lint-test-build: - # release: true - - build-and-test - -jobs: - # lint-test-build: - # description: | - # Check the rust sub section - # executor: - # name: default - # tag: << parameters.version >> - # parameters: - # cache_version: - # default: v1 - # description: Cache version to use - increment this to build a fresh cache. - # type: string - # clippy_arguments: - # default: "" - # description: Arguments to pass to cargo run clippy. - # type: string - # release: - # default: false - # description: Whether to build the binary for release or debug. - # type: boolean - # version: - # default: 1.57.0 - # description: Version of Rust executor to utilize. - # type: string - # with_cache: - # default: true - # description: Whether to restore and save the cache or not - set to no if running multiple commands in one job. - # type: boolean - # working_directory: - # default: ~/robyn - # description: Path to the directory containing your Cargo.lock file. Not needed if Cargo.lock lives in the root. - # type: string - # steps: - # - checkout: - # path: /home/circleci/robyn - # - run: - # name: Update clippy - # command: | - # rustup update - # rustup component add clippy-preview - - # - when: - # condition: <> - # steps: - # - restore_cache: - # keys: - # - cargo-<>-{{ checksum "Cargo.lock" }} - - # - clippy: - # flags: <> - # with_cache: false - # working_directory: <> - # - test: - # with_cache: false - # working_directory: <> - # # command: cargo test - # # - build: - # # release: <> - # # with_cache: false - # # working_directory: <> - # - when: - # condition: <> - # steps: - # - save_cache: - # key: cargo-<>-{{ checksum "Cargo.lock" }} - # paths: - # - ~/.cargo - # working_directory: <> - - build-and-test: # This is the name of the job, feel free to change it to better match what you're trying to do! - # These next lines defines a Docker executors: https://circleci.com/docs/2.0/executor-types/ - # You can specify an image from Dockerhub or use one of the convenience images from CircleCI's Developer Hub - # A list of available CircleCI Docker convenience images are available here: https://circleci.com/developer/images/image/cimg/python - # The executor is the environment in which the steps below will be executed - below will use a python 3.9 container - # Change the version below to your required version of python - docker: - - image: cimg/python:3.8 - # Checkout the code as the first step. This is a dedicated CircleCI step. - # The python orb's install-packages step will install the dependencies from a Pipfile via Pipenv by default. - # Here we're making sure we use just use the system-wide pip. By default it uses the project root's requirements.txt. - # Then run your tests! - # CircleCI will report the results back to your VCS provider. - steps: - - checkout - - python/install-packages: - pkg-manager: pip - app-dir: ~/project # If you're requirements.txt isn't in the root directory. - pip-dependency-file: robyn/test-requirements.txt # if you have a different name for your requirements file, maybe one that combines your runtime and test requirements. - - run: - name: Run tests - # This assumes pytest is installed via the install-package step above - # https://github.com/rust-lang/rustup/issues/297#issuecomment-589989163 - command: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - source $HOME/.cargo/env - maturin build -i python --release --universal2 --out dist - pip install --force-reinstall dist/robyn*.whl - pytest ~/project/integration_tests diff --git a/.github/workflows/CI.yml b/.github/workflows/build-CI.yml similarity index 98% rename from .github/workflows/CI.yml rename to .github/workflows/build-CI.yml index cb9d998ec..875395046 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/build-CI.yml @@ -1,4 +1,6 @@ -name: CI +# CI to build the project for Linux, Windows, and MacOS + +name: Build CI on: push: diff --git a/.github/workflows/python-CI.yml b/.github/workflows/python-CI.yml new file mode 100644 index 000000000..bb78bf079 --- /dev/null +++ b/.github/workflows/python-CI.yml @@ -0,0 +1,33 @@ +# CI to build the Python code + +on: [push, pull_request] + +name: Python Continuous integration + +jobs: + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r robyn/test-requirements.txt + curl https://sh.rustup.rs -sSf | sh -s -- -y + - name: Setup Rust part of the project + run: | + source $HOME/.cargo/env + maturin build -i python --release --universal2 --out dist --no-sdist + pip install --force-reinstall dist/robyn*.whl + - name: Test with pytest + run: | + pytest ./integration_tests \ No newline at end of file diff --git a/.github/workflows/rust-CI.yml b/.github/workflows/rust-CI.yml index f388b27dd..887858b99 100644 --- a/.github/workflows/rust-CI.yml +++ b/.github/workflows/rust-CI.yml @@ -1,3 +1,5 @@ +# CI to build, test, format, and lint the Rust code + on: [push, pull_request] name: Rust Continuous integration diff --git a/robyn/test-requirements.txt b/robyn/test-requirements.txt index 81807f907..d761755a3 100644 --- a/robyn/test-requirements.txt +++ b/robyn/test-requirements.txt @@ -1,5 +1,5 @@ pytest==6.2.5 -maturin +maturin==0.12.11 watchdog requests==2.26.0 uvloop==0.16.0 From 146f49cbd848875b5929be3fb2577a3d38b28415 Mon Sep 17 00:00:00 2001 From: Sanskar Jethi Date: Thu, 21 Jul 2022 21:33:15 +0100 Subject: [PATCH 2/7] Remove CircleCI --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9b8da405f..cda8d6711 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ # Robyn -[![sansyrox](https://circleci.com/gh/sansyrox/robyn.svg?style=svg)](https://app.circleci.com/pipelines/github/sansyrox/robyn) [![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/robyn_oss) [![Gitter](https://badges.gitter.im/robyn_/community.svg)](https://gitter.im/robyn_/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Downloads](https://static.pepy.tech/personalized-badge/robyn?period=total&units=international_system&left_color=grey&right_color=blue&left_text=Downloads)](https://pepy.tech/project/robyn) From c287e3d2f1eed74e0e5d120563f1c23566ec8848 Mon Sep 17 00:00:00 2001 From: Bart Broere Date: Sun, 24 Jul 2022 16:01:46 +0200 Subject: [PATCH 3/7] Fix typo in the README (#246) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cda8d6711..37eab41a5 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ from robyn import Robyn app = Robyn(__file__) @app.get("/") -async def h(requests): +async def h(request): return "Hello, world!" app.start(port=5000) From ab72a66b4a7ca030a45a3e4f5d1680ad8468720f Mon Sep 17 00:00:00 2001 From: Sanskar Jethi Date: Sun, 24 Jul 2022 21:42:32 +0000 Subject: [PATCH 4/7] Add discord badge --- .github/workflows/build-CI.yml | 1 - README.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-CI.yml b/.github/workflows/build-CI.yml index 875395046..923dac13f 100644 --- a/.github/workflows/build-CI.yml +++ b/.github/workflows/build-CI.yml @@ -187,4 +187,3 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | pip install --upgrade twine - twine upload --skip-existing * diff --git a/README.md b/README.md index 37eab41a5..f5f5e4fdc 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![GitHub tag](https://img.shields.io/github/tag/sansyrox/robyn?include_prereleases=&sort=semver&color=black)](https://github.com/sansyrox/robyn/releases/) [![License](https://img.shields.io/badge/License-BSD_2.0-black)](#license) +[![Discord](https://img.shields.io/discord/999782964143603713?label=discord&logo=discord&logoColor=white&style=for-the-badge&color=blue)](https://discord.gg/qKF5sSnC) [![view - Documentation](https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge)](https://sansyrox.github.io/robyn/#/) From 2a3cf25a35374b8bbe979f0f6be876c09aee7c28 Mon Sep 17 00:00:00 2001 From: Sanskar Jethi Date: Sat, 6 Aug 2022 16:35:52 +0100 Subject: [PATCH 5/7] ci: add github actions to publish every PR on test pypi (#259) --- .github/workflows/build-CI.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/build-CI.yml b/.github/workflows/build-CI.yml index 923dac13f..f7cb0c7ac 100644 --- a/.github/workflows/build-CI.yml +++ b/.github/workflows/build-CI.yml @@ -187,3 +187,23 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | pip install --upgrade twine + twine upload --skip-existing * + + preview_release: + name: Preview Release + runs-on: ubuntu-latest + needs: [macos, windows, linux, linux-cross] + steps: + - uses: actions/download-artifact@v3 + with: + name: wheels + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - name: Publish to PyPi + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TEST_PYPI_PASSWORD }} + run: | + pip install --upgrade twine + twine upload -r testpypi --skip-existing * From 787ec9353b1c703cb6c474226eae1188f4f55f59 Mon Sep 17 00:00:00 2001 From: Sanskar Jethi Date: Sat, 6 Aug 2022 16:36:04 +0100 Subject: [PATCH 6/7] ci: enable fail fast for faster response time in the pipelines (#260) --- .github/workflows/build-CI.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-CI.yml b/.github/workflows/build-CI.yml index f7cb0c7ac..5cf89ea9a 100644 --- a/.github/workflows/build-CI.yml +++ b/.github/workflows/build-CI.yml @@ -118,7 +118,6 @@ jobs: linux-cross: runs-on: ubuntu-latest strategy: - fail-fast: false matrix: python: [ From 5fa1db21a4d6aba1acf53a91ffd49a48921a5c8b Mon Sep 17 00:00:00 2001 From: Sanskar Jethi Date: Thu, 11 Aug 2022 21:56:21 +0100 Subject: [PATCH 7/7] fix robyn on windows - remove process forking(#261) * chore: Add more type info * Fix multiprocessing in windows * Try apt instead of apt-get * fix: remove multiprocessing from windwos TODO: check how everything behaves on passing the --processes flag * fix: join unstarted process on windows * fix(__init__.py): hopefully fixes windows launch * rename to the final version --- .github/workflows/build-CI.yml | 8 ++--- Cargo.lock | 2 +- Cargo.toml | 2 +- pyproject.toml | 2 +- robyn/__init__.py | 60 +++++++++++++++++++++++----------- robyn/__init__.pyi | 16 ++++----- robyn/processpool.py | 37 ++++++++++++--------- robyn/robyn.pyi | 1 + 8 files changed, 79 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build-CI.yml b/.github/workflows/build-CI.yml index 5cf89ea9a..7bb1cc218 100644 --- a/.github/workflows/build-CI.yml +++ b/.github/workflows/build-CI.yml @@ -147,12 +147,12 @@ jobs: dockerRunArgs: | --volume "${PWD}/dist:/artifacts" install: | - apt-get update -y - apt-get install -y --no-install-recommends software-properties-common + apt update -y + apt install -y --no-install-recommends software-properties-common add-apt-repository ppa:deadsnakes/ppa - apt-get update -y + apt update -y PYTHON=python${{ matrix.python.version }} - apt-get install -y $PYTHON $PYTHON-distutils $PYTHON-venv + apt install -y $PYTHON $PYTHON-distutils $PYTHON-venv run: | ls -lrth /artifacts PYTHON=python${{ matrix.python.version }} diff --git a/Cargo.lock b/Cargo.lock index d209c2239..fc4831b4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1229,7 +1229,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "robyn" -version = "0.17.1" +version = "0.17.2" dependencies = [ "actix", "actix-files", diff --git a/Cargo.toml b/Cargo.toml index d2842c35e..50813495a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "robyn" -version = "0.17.1" +version = "0.17.2" authors = ["Sanskar Jethi "] edition = "2018" description = "A web server that is fast!" diff --git a/pyproject.toml b/pyproject.toml index d6442bb38..5a785df5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ name = "robyn" -version = "0.17.1" +version = "0.17.2" description = "A web server that is fast!" authors = ["Sanskar Jethi "] diff --git a/robyn/__init__.py b/robyn/__init__.py index 2238c5a50..d6013932a 100644 --- a/robyn/__init__.py +++ b/robyn/__init__.py @@ -2,21 +2,20 @@ import logging import multiprocessing as mp import os +import sys from multiprocess import Process from watchdog.observers import Observer from robyn.events import Events -from .argument_parser import ArgumentParser -from .dev_event_handler import EventHandler -from .log_colors import Colors -from .processpool import spawn_process -from .responses import jsonify, static_file +from robyn.argument_parser import ArgumentParser +from robyn.dev_event_handler import EventHandler +from robyn.log_colors import Colors +from robyn.processpool import spawn_process +from robyn.responses import jsonify, static_file -from .robyn import SocketHeld -from .router import MiddlewareRouter, Router, WebSocketRouter -from .ws import WS - -mp.allow_connection_pickling() +from robyn.robyn import SocketHeld +from robyn.router import MiddlewareRouter, Router, WebSocketRouter +from robyn.ws import WS logger = logging.getLogger(__name__) @@ -104,14 +103,26 @@ def start(self, url="127.0.0.1", port=5000): :param port int: reperesents the port number at which the server is listening """ + def init_processpool(socket): + + process_pool = [] + if sys.platform.startswith("win32"): + spawn_process( + self.directories, + self.headers, + self.router.get_routes(), + self.middleware_router.get_routes(), + self.web_socket_router.get_routes(), + self.event_handlers, + socket, + workers, + ) + + return process_pool - if not self.dev: - processes = [] - workers = self.workers - socket = SocketHeld(url, port) for _ in range(self.processes): copied_socket = socket.try_clone() - p = Process( + process = Process( target=spawn_process, args=( self.directories, @@ -124,18 +135,29 @@ def start(self, url="127.0.0.1", port=5000): workers, ), ) - p.start() - processes.append(p) + process.start() + process_pool.append(process) + + return process_pool + + mp.allow_connection_pickling() + + if not self.dev: + workers = self.workers + socket = SocketHeld(url, port) + + process_pool = init_processpool(socket) logger.info(f"{Colors.HEADER}Starting up \n{Colors.ENDC}") logger.info(f"{Colors.OKGREEN}Press Ctrl + C to stop \n{Colors.ENDC}") try: - for process in processes: + for process in process_pool: process.join() except KeyboardInterrupt: logger.info(f"{Colors.BOLD}{Colors.OKGREEN} Terminating server!! {Colors.ENDC}") - for process in processes: + for process in process_pool: process.kill() + else: event_handler = EventHandler(self.file_path) event_handler.start_server_first_time() diff --git a/robyn/__init__.pyi b/robyn/__init__.pyi index 3c8f50fc4..cb1762404 100644 --- a/robyn/__init__.pyi +++ b/robyn/__init__.pyi @@ -9,14 +9,14 @@ from watchdog.observers import Observer from robyn.events import Events -from .argument_parser import ArgumentParser -from .dev_event_handler import EventHandler -from .log_colors import Colors -from .processpool import spawn_process -from .responses import jsonify, static_file -from .robyn import Server, SocketHeld -from .router import MiddlewareRouter, Router, WebSocketRouter -from .ws import WS +from robyn.argument_parser import ArgumentParser +from robyn.dev_event_handler import EventHandler +from robyn.log_colors import Colors +from robyn.processpool import spawn_process +from robyn.responses import jsonify, static_file +from robyn.robyn import Server, SocketHeld +from robyn.router import MiddlewareRouter, Router, WebSocketRouter +from robyn.ws import WS class Robyn: diff --git a/robyn/processpool.py b/robyn/processpool.py index ed1db0ed5..0f34040c4 100644 --- a/robyn/processpool.py +++ b/robyn/processpool.py @@ -1,13 +1,30 @@ import asyncio import logging import multiprocessing as mp +from multiprocessing.process import AuthenticationString import sys -from .events import Events -from .robyn import Server +from robyn.events import Events +from robyn.robyn import Server +from copy import deepcopy -mp.allow_connection_pickling() + +def initialize_event_loop(): + # platform_name = platform.machine() + if sys.platform.startswith("win32") or sys.platform.startswith("linux-cross"): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + return loop + else: + # uv loop doesn't support windows or arm machines at the moment + # but uv loop is much faster than native asyncio + import uvloop + + uvloop.install() + loop = uvloop.new_event_loop() + asyncio.set_event_loop(loop) + return loop def spawn_process( @@ -28,18 +45,7 @@ def spawn_process( :param workers number: This is the name given to the process to identify the process """ - # platform_name = platform.machine() - if sys.platform.startswith("win32") or sys.platform.startswith("linux-cross"): - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - else: - # uv loop doesn't support windows or arm machines at the moment - # but uv loop is much faster than native asyncio - import uvloop - - uvloop.install() - loop = uvloop.new_event_loop() - asyncio.set_event_loop(loop) + loop = initialize_event_loop() server = Server() @@ -81,3 +87,4 @@ def spawn_process( loop.run_forever() except KeyboardInterrupt: loop.close() + diff --git a/robyn/robyn.pyi b/robyn/robyn.pyi index fd4d0de74..94e648636 100644 --- a/robyn/robyn.pyi +++ b/robyn/robyn.pyi @@ -28,6 +28,7 @@ class Server: handler: Callable, is_async: bool, number_of_params: int, + const: bool, ) -> None: pass def add_middleware_route(