nodePythonCollaboration 0.0.3
Those simple Node and Python packages with will let you easy run Python for Node

# Colab

Those simple Node and Python packages with will let you easy run Python for Node

*Builded on top of [zeroRPC](https://www.zerorpc.io/)*

To run this package corectly you need to use both `node` and `python` versions

## Features

- Call Python functions from node
- Decorate Python functions to get better results

## Installing

Using npm:

```bash
$ npm install node-python-collaboration
```

Using pip:

```bash
$ pip install nodePythonCollaboration
```

## Quick start 

Node client side (with sync setup)

```js
const colab = require('node-python-collaboration')

// Configure methods object synchronously
const methods = colab([
    ['test_method', false],
],
/*options*/
)

// Call some methods
methods.test_method(1)
    .then(res => console.log(res)) // logs 2
    .catch(err => console.log(err)) 
```

Node client side (with async setup)

> **Note** that in async setup client get methods from python server

```js
const colab = require('node-python-collaboration')

async function main() {
    try {
        // Configure methods object asynchronously
        const methods = await colab.asyncInit(/*options*/)

        // Call some methods
        console.log(await methods.test_method(1)) // logs 2

    } catch(err) {
        console.log(err)
    }
}

main()
```

Python server side

```python
import nodePythonCollaboration as colab

# Define main class with methods
class main():
    def test_method(self,x):
    return x + 1

# Start Python server with those methods
colab.start(main)
```

## Colab functions

### In Node.js:

<br/>

#### colab(methodsArr, { options })


Returns an object with properties that are functions with the names given in method_name

`methodsArr`
```js
    [
        [method_name, method_type],
        [method_name, method_type]
        ...
    ]
```
`options` optional **need to match up with python ones**
```js
    // those are default options
    {
        port: '4242',
        server: '127.0.0.1' // it is recommended not to change
    }
```
`Example`
```js
    methods = colab([
        ['test_method', false],
        ['test_method_2', false]
    ],{
        port: '4242'
    })
    // Do some stuff with methods
```
*OR*
```js
    methods = colab([
        ['test_method', false],
        ['test_method_2', false]
    ])
    // Do some stuff with methods
```

<br/>

#### colab.asyncInit({ options })

Returns a promise when promise is resolved promise returns methods

In async init we don't need to give methodsArr because `colab` asks for available methods our `Python server`

`Example`
```js
    colab.asyncInit(/*options*/)
        .then((methods) => {
            // Do some stuff with methods
        })
```
*better way to do this async/await*
```js
async function main() {
    methods = await colab.asyncInit(/*options*/)
    // Do some stuff with methods

}

main()
```

<br/>

#### methods

- Types

    - false

        *the fastest*
        ```js
        [
            [method_name, false]
        ]
        ```
        This is just normal method nothing special

    - complexFile

        *slower*
        ```js
        [
            [method_name, 'complexFile']
        ]
        ```
        If you are getting error "OverflowError: Python int too large to convert to C unsigned long" <br/>
        then this is the solution **remember that on python side you need to use method decorator**

        Basically what it does: python saves the output to the file located in /data folder and then sends the name of the file to node
        so node can read the file and then delete it

    - bigComputation

        *the slowest*
        ```js
        [
            [method_name, 'bigComputation']
        ]
        ```
        If you are getting error "HeartbeatError: Lost remote after 10000ms"<br/>
        then this is the solution **remember that on python side you need to use method decorator**

        Basically what it does: python runs your function in another process so it don't bock the main process
        then it saves the output to the file 

- Overview
    - returns a promise
    - executes python function `in python`
    - arguments given to the method are passed to the python

- Example
    ```js
    const colab = require('node-python-collaboration')
    colab([
        ['test_method',false]
    ])
    .then(methods => {
        methods.test_method(1)
        .then(response => console.log(response)) //logs 2
    })
    ```
    *better way to do this async/await*
    ```js
    const colab = require('node-python-collaboration')

    async function main() {
        methods = await colab.asyncInit()
        console.log(methods.test_method(1)) //logs 2
    }
    main()
    ```
<br/>

### In Python:

<br/>

#### colab.start(class, optionsArr)

Starts up python server with methods in class default on 127.0.0.1:4242

`class`
```python
class main():
    def test_method(self,x):
    return x + 1
```
now method "test_method" is available

`optionsArr` optional

```python
optionsArr = [ data_folder_path, server_ip_and_port ]

#defining new data flder path
optionsArr = [ './test/test/data' ]

#defining new server port (data folder path stay default)
optionsArr = [ None, ':5323']

#defining new server ip and port (data folder path stay default)
optionsArr = [ None, '0.0.0.0:5323'] # it's very not recommeded to change ip, in most cases it won't run
```

<br/>

### colab decorators

They are described above in node section
- @colab.complexFile
- @colab.bigComputation

`Example`
```python
import nodePythonCollaboration as colab
from math import factorial
from time import sleep

class main():

    def Example(self, x, y):
        return x + y # Just simple computation

    @colab.complexFile
    def Example_complexFile(self, x):
        return factorial(x) # In most cases that will be very big number

    @colab.bigComputation
    def Example_bigComputation(self, x, y):
        for i in range(11):
            print(i + 1)
            sleep(1) # Simulating big computation
        return x + y


colab.start(main)
```

### Node and Python:

`Example`

*server code*
```python
import nodePythonCollaboration as colab # import colab
from math import factorial
from time import sleep

class main(): # define methods

    def Example(self, x, y):
        return x + y 

    @colab.complexFile
    def Example_complexFile(self, x):
        return factorial(x)

    @colab.bigComputation
    def Example_bigComputation(self, x, y):
        for i in range(11):
            print(i + 1)
            sleep(1)
        return x + y


colab.start(main) # start server with default options
```

*client code*
```js
const colab = require('node-python-collaboration') // import colab

async function main() {
    try {
        // Configure methods object asynchronously with default options
        const methods = await colab.asyncInit()

        console.log(await methods.Example(2, 2)) //logs 4
        console.log(await methods.Example_complexFile(10000)) //logs 2846259680917054518906413212119868890148051401702799230794179994274411340003764443772990786757784775815884062142317528830042339940...
        console.log(await methods.Example_bigComputation(2, 2)) //after 11s logs 4

    } catch(err) {
        console.log(err)
    }
}

main()
```