b3dnet


Nameb3dnet JSON
Version 0.0.1 PyPI version JSON
download
home_page
SummaryTCP Requests to communicate with local realtime applications.
upload_time2023-05-11 13:29:17
maintainer
docs_urlNone
author
requires_python>=3.7
license
keywords tcp server local realtime
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # B3DNet - TCP Server Requests


TCP setup for realime applications on local machines. As code can get executed based on client side requests, this implementation is not considered to be safe. Only execute Tasks from sources that you trust.<br>
**Build on top of socketserver and multiprocessing with the main goal to remote control blender via TCP.**


## Why not use Pickle?

The goal was, to give the ability to communicate from other languages. <br>
In a nutshell, Tasks are jsons with a python function as string!<br>
```
{
  "flag": 16,
  "idname": "UNIQUE_FUNCTION_ID",
  "func": """def some_fn(*args, **kwargs):\n\tprint("Hello world")""",
  "args": ["args", 21],
  "kwargs": {"hello": "world"}
}
```


## Usage

Setup the threaded TCPServer to receive and execute tasks:

```
import queue
from b3dnet.connection import TCPServer, SERVER

q = queue.Queue()
server = TCPServer("localhost", 6000, q, b'')
server.connect(timeout=10.0)

# receive tasks
while server.flag & SERVER.CONNECTED:
    try:
        task = q.get(timeout=1.0, block=True)
    except queue.Empty:
        task = None
        break

    if task:
        q.task_done()

# flush queue
while not q.empty():
    task = q.get(timeout=QUEUE_TIMEOUT)
    if task is None:
        break
    task.execute()
    q.task_done()
```

Create and send tasks using a client:

```
import queue
from b3dnet.connection import TCPClient, CLIENT
from b3dnet.request import *
 
# connect the client
client = TCPClient("localhost", 6000, b'secret_key')
client.connect()


# function which should be passed
def hello_world(*args, **kwargs):
    print("Method from client which prints!", args, kwargs)

# register and call function
register_func = Task(
    (TASK.NEW_FN | TASK.CALL_FN), 'HELLO_WORLD_FN', hello_world
)
client.send(register_func)

# call the function using args
for i in range(0, 1000):
    call_data = Task(
        TASK.CALL_FN, 'HELLO_WORLD_FN', None,
        f"args_{i}", kwargs=f"kwargs_{i}")
    client.send(call_data)

# shutdown or restart the server request
client.send(Task((TASK.SHUTDOWN | TASK.CLEAR_CACHE), ))
# client.send(Task((TASK.RESTART)))
```

Lets extend this and expect we restarted the server:

```
# (re)connect the client
client = TCPClient("localhost", 6000, b'secret_key')
client.connect()

# create a list object
req = Task(TASK.NEW_OB, "OBJECT_ID", list)
client.send(req)

def fn_set(*args):
    return [i for i in list(args)]
 
# add args to the list using some fn 
# (fns and objs are in the same cache so naming matters)
args = [1, 2, 4, 3, 9, 6, 21]
req = Task(
    # it's possible to chain multiple tasks
    (TASK.NEW_FN | TASK.CALL_FN | TASK.DEL_FN | TASK.SET_OB), 
    ["FN_SET_ID", "OBJECT_ID"], fn_set,
    *args
)
client.send(req)

# some fn that takes args
def fn_ob_as_arg(*args):
    x = 0
    for arg in args:
        if isinstance(arg, int):
            x += arg
        if isinstance(arg, list):
            x += fn_ob_as_arg(*arg)
    return x

# send object as args (may use multiple objects)
req = Task(
    (TASK.NEW_FN | TASK.CALL_FN | TASK.OB_AS_ARG | TASK.DEL_FN),
    ["FN_SUM_ID", "OBJECT_ID", "OBJECT_ID"],
    fn_ob_as_arg, 10, 10, 10
)
client.send(req)
```


## Task Concept

Using a dict to register functions and objs on the Listener side which then can be called from the Client side. <br>
Once a function or ob isn't required anymore, consider to unregister it. <br>
On the server side, functions may be registered when starting the application which can be called directly. <br>
To do so, add functions to the CACHE dict. <br>
If you do, consider to start the id name with 'PERSISTENT' so they don't get removed on restart or shutdown of the server. 

Available flags:
```
TASK.NEW_FN             # Register a function with an unique idname
TASK.CALL_FN            # Call a function using its idname
TASK.DEL_FN             # Unregister a function

TASK.NEW_OB             # Register an ob
TASK.SET_OB             # Set an ob by a fn call
TASK.DEL_OB             # Unregister an ob
TASK.OB_AS_ARG:         # Use an ob as arg
TASK.OB_AS_KWARG:       # Use an ob with its idname as kwarg

TASK.PASSTHOUGH         # Do nothing (useful when using client on a seperate thread which pull from queue)
TASK.SHUTDOWN           # Shutdown the server and client
TASK.RESTART            # Restart the server and shutdown client

TASK.CLEAR_CACHE        # Clear all cached functions (also persistent ones)
```

Modules which can be used by default (others may get filtered out). <br>
You can overwrite this on the server & client side if necessary.
-> b3dnet.request.MODULES = [...]

```
'bpy', 'mathutils', 'bvhtree', 'bmesh', 'bpy_types', 'numpy',
'bpy_extras', 'bl_ui', 'bl_operators', 'bl_math', 'bisect', 'math'
```

## Developer nodes

Everything is based around the Tasks. <br>
Once started, the server waits for x-seconds for incoming connections and shutsdown if no connection has been established in time. Then the server basically servers forever in a separete thread, shutsdown and restarts if asked to. All Tasks the server receives are staged in a queue which may be accessed from another thread. <br>
The Client is basically just a multiprocessing client which uses Task object. <br>

```
# setup venv
python3 -m venv .venv
source .venv/bin/activate

# install optional requirements
pip install pytest
pip install mypi
pip install --upgrade build

# run tests
python3 -m pytest

# generate stubs if you make major changes
stubgen src/b3dnet

# build package
python3 -m build
```

# License
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Copyright (C) Denys Hsu - cgtinker, cgtinker.com, hello@cgtinker.com



            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "b3dnet",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "\"D.Hsu - cgtinker\" <hello@cgtinker.com>",
    "keywords": "tcp,server,local,realtime",
    "author": "",
    "author_email": "\"D.Hsu - cgtinker\" <hello@cgtinker.com>",
    "download_url": "https://files.pythonhosted.org/packages/40/1f/f11d6dadd4cc0a3b8122f42e4fdbbf3f3f744baea103ef6d62c3fba79213/b3dnet-0.0.1.tar.gz",
    "platform": null,
    "description": "# B3DNet - TCP Server Requests\n\n\nTCP setup for realime applications on local machines. As code can get executed based on client side requests, this implementation is not considered to be safe. Only execute Tasks from sources that you trust.<br>\n**Build on top of socketserver and multiprocessing with the main goal to remote control blender via TCP.**\n\n\n## Why not use Pickle?\n\nThe goal was, to give the ability to communicate from other languages. <br>\nIn a nutshell, Tasks are jsons with a python function as string!<br>\n```\n{\n  \"flag\": 16,\n  \"idname\": \"UNIQUE_FUNCTION_ID\",\n  \"func\": \"\"\"def some_fn(*args, **kwargs):\\n\\tprint(\"Hello world\")\"\"\",\n  \"args\": [\"args\", 21],\n  \"kwargs\": {\"hello\": \"world\"}\n}\n```\n\n\n## Usage\n\nSetup the threaded TCPServer to receive and execute tasks:\n\n```\nimport queue\nfrom b3dnet.connection import TCPServer, SERVER\n\nq = queue.Queue()\nserver = TCPServer(\"localhost\", 6000, q, b'')\nserver.connect(timeout=10.0)\n\n# receive tasks\nwhile server.flag & SERVER.CONNECTED:\n    try:\n        task = q.get(timeout=1.0, block=True)\n    except queue.Empty:\n        task = None\n        break\n\n    if task:\n        q.task_done()\n\n# flush queue\nwhile not q.empty():\n    task = q.get(timeout=QUEUE_TIMEOUT)\n    if task is None:\n        break\n    task.execute()\n    q.task_done()\n```\n\nCreate and send tasks using a client:\n\n```\nimport queue\nfrom b3dnet.connection import TCPClient, CLIENT\nfrom b3dnet.request import *\n \n# connect the client\nclient = TCPClient(\"localhost\", 6000, b'secret_key')\nclient.connect()\n\n\n# function which should be passed\ndef hello_world(*args, **kwargs):\n    print(\"Method from client which prints!\", args, kwargs)\n\n# register and call function\nregister_func = Task(\n    (TASK.NEW_FN | TASK.CALL_FN), 'HELLO_WORLD_FN', hello_world\n)\nclient.send(register_func)\n\n# call the function using args\nfor i in range(0, 1000):\n    call_data = Task(\n        TASK.CALL_FN, 'HELLO_WORLD_FN', None,\n        f\"args_{i}\", kwargs=f\"kwargs_{i}\")\n    client.send(call_data)\n\n# shutdown or restart the server request\nclient.send(Task((TASK.SHUTDOWN | TASK.CLEAR_CACHE), ))\n# client.send(Task((TASK.RESTART)))\n```\n\nLets extend this and expect we restarted the server:\n\n```\n# (re)connect the client\nclient = TCPClient(\"localhost\", 6000, b'secret_key')\nclient.connect()\n\n# create a list object\nreq = Task(TASK.NEW_OB, \"OBJECT_ID\", list)\nclient.send(req)\n\ndef fn_set(*args):\n    return [i for i in list(args)]\n \n# add args to the list using some fn \n# (fns and objs are in the same cache so naming matters)\nargs = [1, 2, 4, 3, 9, 6, 21]\nreq = Task(\n    # it's possible to chain multiple tasks\n    (TASK.NEW_FN | TASK.CALL_FN | TASK.DEL_FN | TASK.SET_OB), \n    [\"FN_SET_ID\", \"OBJECT_ID\"], fn_set,\n    *args\n)\nclient.send(req)\n\n# some fn that takes args\ndef fn_ob_as_arg(*args):\n    x = 0\n    for arg in args:\n        if isinstance(arg, int):\n            x += arg\n        if isinstance(arg, list):\n            x += fn_ob_as_arg(*arg)\n    return x\n\n# send object as args (may use multiple objects)\nreq = Task(\n    (TASK.NEW_FN | TASK.CALL_FN | TASK.OB_AS_ARG | TASK.DEL_FN),\n    [\"FN_SUM_ID\", \"OBJECT_ID\", \"OBJECT_ID\"],\n    fn_ob_as_arg, 10, 10, 10\n)\nclient.send(req)\n```\n\n\n## Task Concept\n\nUsing a dict to register functions and objs on the Listener side which then can be called from the Client side. <br>\nOnce a function or ob isn't required anymore, consider to unregister it. <br>\nOn the server side, functions may be registered when starting the application which can be called directly. <br>\nTo do so, add functions to the CACHE dict. <br>\nIf you do, consider to start the id name with 'PERSISTENT' so they don't get removed on restart or shutdown of the server. \n\nAvailable flags:\n```\nTASK.NEW_FN             # Register a function with an unique idname\nTASK.CALL_FN            # Call a function using its idname\nTASK.DEL_FN             # Unregister a function\n\nTASK.NEW_OB             # Register an ob\nTASK.SET_OB             # Set an ob by a fn call\nTASK.DEL_OB             # Unregister an ob\nTASK.OB_AS_ARG:         # Use an ob as arg\nTASK.OB_AS_KWARG:       # Use an ob with its idname as kwarg\n\nTASK.PASSTHOUGH         # Do nothing (useful when using client on a seperate thread which pull from queue)\nTASK.SHUTDOWN           # Shutdown the server and client\nTASK.RESTART            # Restart the server and shutdown client\n\nTASK.CLEAR_CACHE        # Clear all cached functions (also persistent ones)\n```\n\nModules which can be used by default (others may get filtered out). <br>\nYou can overwrite this on the server & client side if necessary.\n-> b3dnet.request.MODULES = [...]\n\n```\n'bpy', 'mathutils', 'bvhtree', 'bmesh', 'bpy_types', 'numpy',\n'bpy_extras', 'bl_ui', 'bl_operators', 'bl_math', 'bisect', 'math'\n```\n\n## Developer nodes\n\nEverything is based around the Tasks. <br>\nOnce started, the server waits for x-seconds for incoming connections and shutsdown if no connection has been established in time. Then the server basically servers forever in a separete thread, shutsdown and restarts if asked to. All Tasks the server receives are staged in a queue which may be accessed from another thread. <br>\nThe Client is basically just a multiprocessing client which uses Task object. <br>\n\n```\n# setup venv\npython3 -m venv .venv\nsource .venv/bin/activate\n\n# install optional requirements\npip install pytest\npip install mypi\npip install --upgrade build\n\n# run tests\npython3 -m pytest\n\n# generate stubs if you make major changes\nstubgen src/b3dnet\n\n# build package\npython3 -m build\n```\n\n# License\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCopyright (C) Denys Hsu - cgtinker, cgtinker.com, hello@cgtinker.com\n\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "TCP Requests to communicate with local realtime applications.",
    "version": "0.0.1",
    "project_urls": {
        "Bug Tracker": "https://github.com/cgtinker/TCPRequests/issues",
        "Homepage": "https://github.com/cgtinker/TCPRequests"
    },
    "split_keywords": [
        "tcp",
        "server",
        "local",
        "realtime"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "deed867a076327f14eef5d886ddc52e016d8eae04a06b6fd91a5abc7ae300801",
                "md5": "f721c7af6a0a38e88e0e0ba06e821219",
                "sha256": "d9afb1143c95ab8832760c3ad4c544268ffbbae84a7ad07f83169c25a73f902e"
            },
            "downloads": -1,
            "filename": "b3dnet-0.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "f721c7af6a0a38e88e0e0ba06e821219",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 19390,
            "upload_time": "2023-05-11T13:29:13",
            "upload_time_iso_8601": "2023-05-11T13:29:13.661508Z",
            "url": "https://files.pythonhosted.org/packages/de/ed/867a076327f14eef5d886ddc52e016d8eae04a06b6fd91a5abc7ae300801/b3dnet-0.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "401ff11d6dadd4cc0a3b8122f42e4fdbbf3f3f744baea103ef6d62c3fba79213",
                "md5": "00ec5f0b26d8deabf1359b095728ffa4",
                "sha256": "32c9f3678c5663e8ba810d9693f32c2e37addc02fc54c20a51c2d0e312678e14"
            },
            "downloads": -1,
            "filename": "b3dnet-0.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "00ec5f0b26d8deabf1359b095728ffa4",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 19297,
            "upload_time": "2023-05-11T13:29:17",
            "upload_time_iso_8601": "2023-05-11T13:29:17.210010Z",
            "url": "https://files.pythonhosted.org/packages/40/1f/f11d6dadd4cc0a3b8122f42e4fdbbf3f3f744baea103ef6d62c3fba79213/b3dnet-0.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-05-11 13:29:17",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "cgtinker",
    "github_project": "TCPRequests",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "b3dnet"
}
        
Elapsed time: 0.09283s