github.com/craicoverflow/tyk@v2.9.6-rc3+incompatible/coprocess/python/README.md (about)

     1  # Coprocess (Python)
     2  
     3  This feature makes it possible to write Tyk middleware using [Python](https://www.python.org/), the current binding supports Python 3.x.
     4  The purpose of this README is to provide an overview of the architecture and a few implementation notes.
     5  
     6  ## Usage
     7  
     8  You'll need to build Tyk with specific build tags, see build notes below.
     9  
    10  Basically `go build -tags 'coprocess python'`.
    11  
    12  ### Setting up custom Python middleware
    13  
    14  The custom middleware should be specified in your API definition file, under `custom_middleware` (see [coprocess_app_sample.json](../../apps/coprocess_app_sample.json)):
    15  
    16  ```json
    17  "custom_middleware": {
    18    "pre": [
    19        {
    20          "name": "MyPreMiddleware",
    21          "require_session": false
    22        }
    23      ],
    24    "post": [
    25      {
    26          "name": "MyPostMiddleware",
    27          "require_session": false
    28      }
    29    ],
    30    "driver": "python"
    31  }
    32  ```
    33  
    34  You can chain multiple hook functions when the hook type is Pre, Post or PostAuthCheck.
    35  
    36  Tyk will load all the modules inside `middleware/python`.
    37  
    38  The "name" field represents the name of a Python function, a sample Python middleware matching the sample definition above will look like (see [middleware/python](../../middleware/python)):
    39  
    40  ```python
    41  from tyk.decorators import *
    42  
    43  @Pre
    44  def MyPreMiddleware(request, session, spec):
    45      print("my_middleware: MyPreMiddleware")
    46      return request, session
    47  
    48  @Post
    49  def MyPreMiddleware(request, session, spec):
    50      print("my_middleware: MyPreMiddleware")
    51      return request, session
    52  ```
    53  
    54  ### Authenticating an API with Python
    55  
    56  See example: https://tyk.io/docs/customise-tyk/plugins/rich-plugins/python/custom-auth-python-tutorial/
    57  
    58  ### Writing events handlers with Python
    59  
    60  It's also possible to write a Tyk event listener with Python. The first step is to set a custom event handler inside your API definition (see [coprocess_app_sample_protected.json](../../apps/coprocess_app_sample_protected.json)):
    61  
    62  ```json
    63  ...
    64  "event_handlers": {
    65    "events": {
    66      "AuthFailure": [
    67        {
    68          "handler_name": "cp_dynamic_handler",
    69          "handler_meta": {
    70            "name": "my_handler"
    71          }
    72        }
    73      ]
    74    }
    75  },
    76  ...
    77  ```
    78  
    79  In the above sample we're setting an event handler for `AuthFailure` events, an event that's triggered everytime a failed authentication occurs.
    80  
    81  The `handler_name` must be `cp_dynamic_handler`.
    82  
    83  The `name` field inside `handler_meta` refers to a Python function name that can be written inside `event_handlers` (see [event_handlers/my_handler.py](../../event_handlers/my_handler.py)):
    84  
    85  ```python
    86  from tyk.decorators import Event
    87  
    88  @Event
    89  def my_handler(event, spec):
    90      print("-- my_handler:")
    91      print(" Event:", event)
    92      print(" Spec:", spec)
    93  ```
    94  
    95  This function will be called when the specified event occurs, Tyk will pass a Python object like this:
    96  
    97  ```json
    98  {  
    99     "TimeStamp": "2016-08-19 11:13:31.537047694 -0400 PYT",
   100     "Meta":{  
   101        "Path":"/coprocess-auth-tyk-api-test/",
   102        "Origin":"127.0.0.1",
   103        "Message":"Auth Failure",
   104        "OriginatingRequest":"R0VUIC9jb3Byb2Nlc3MtYXV0aC10eWstYXBpLXRlc3QvIEhUVFAvMS4xDQpIb3N0OiAxMjcuMC4wLjE6ODA4MA0KVXNlci1BZ2VudDogY3VybC83LjQzLjANCkFjY2VwdDogKi8qDQpBdXRob3JpemF0aW9uOiAxDQoNCg==",
   105        "Key":""
   106     },
   107     "Type": "AuthFailure"
   108  }
   109  ```
   110  
   111  The above handler can be tested by sending a HTTP request to the protected Coprocess API, with an invalid authorization header:
   112  
   113  ```
   114  curl http://127.0.0.1:8080/coprocess-auth-tyk-api-test/ -H 'Authorization: invalidtoken'
   115  ```
   116  
   117  ## Build requirements
   118  
   119  * [Python 3.x](https://www.python.org/)
   120  * [Go](https://golang.org)
   121  * [Cython](http://cython.org/) (required if you need to modify and re-compile the gateway API binding)
   122  * [protobuf](https://pypi.python.org/pypi/protobuf/3.0.0) (Python module): `pip3 install protobuf`
   123  * [grpc](https://www.grpc.io/) (gRPC module): `pip3 install grpcio`
   124  
   125  ## Build steps
   126  
   127  To build Tyk with the Coprocess + Python support, use:
   128  
   129  ```
   130  go build -tags 'coprocess python'
   131  ```
   132  
   133  To compile the gateway API binding (assuming you're on the repository root):
   134  
   135  ```sh
   136  cd coprocess/python
   137  ./cythonize gateway
   138  ```
   139  
   140  This will "cythonize" `gateway.pyx`, generating `gateway.c` and `gateway.h`.
   141  
   142  To compile some other binding (where `mybinding.pyx` is your Cython input file):
   143  
   144  ```sh
   145  cd coprocess/python
   146  ./cythonize mybinding
   147  ```
   148  
   149  [cythonize](cythonize) is a helper script for compiling Python source files with Cython and patching the resulting source with the specific build tags used by this Coprocess feature.
   150  This is important in order to keep Tyk build-able when a standard build is needed (and make the Go compiler ignore C binding files based on the specified build tags).
   151  
   152  The top of a standard Cython binding file will look like this:
   153  ```
   154  /* Generated by Cython 0.24.1 */
   155  
   156  /* BEGIN: Cython Metadata
   157  {
   158      "distutils": {
   159          "depends": []
   160      },
   161      "module_name": "gateway"
   162  }
   163  END: Cython Metadata */
   164  ```
   165  
   166  After running `cythonize` the binding will have the correct build tags, and will be ignored if you don't build Tyk with these (`go build -tags 'coprocess python'`):
   167  
   168  ```
   169  // +build coprocess
   170  // +build python
   171  
   172  /* Generated by Cython 0.24.1 */
   173  
   174  /* BEGIN: Cython Metadata
   175  {
   176      "distutils": {
   177          "depends": []
   178      },
   179      "module_name": "gateway"
   180  }
   181  END: Cython Metadata */
   182  ```
   183  
   184  After re-compiling a binding, the C source code will change and you may want to build Tyk again.
   185  
   186  ## CPython
   187  
   188  [Python](https://www.python.org/) has a very popular and well-documented [C API](https://docs.python.org/3/c-api/index.html), this coprocess feature makes a heavy use of it.
   189  
   190  ## Built-in modules
   191  
   192  All the standard Python modules are available and it's also possible to load additional ones, if you add them to your local Python installation (for example, using pip).
   193  
   194  ### Coprocess Gateway API
   195  
   196  There's a Python binding for the [Coprocess Gateway API](../README.md), this is written using the Cython syntax, it's basically a single file: [`gateway.pyx`](tyk/gateway.pyx).
   197  
   198  This binding exposes some functions, like a storage handler that allows you to get/set Redis keys:
   199  
   200  ```python
   201  from tyk.decorators import *
   202  from gateway import TykGateway as tyk
   203  
   204  @Pre
   205  def SetKeyOnRequest(request, session, spec):
   206      tyk.store_data( "my_key", "expiring_soon", 15 )
   207      val = tyk.get_data("cool_key")
   208      return request, session
   209  ```
   210  
   211  ### Cython bindings
   212  
   213  Cython takes a `.pyx` file and generates a C source file (`.c`) with its corresponding header (`.h`), after this process, we use these two files as part of the `cgo` build process. This approach has been used as an alternative to `cffi`, which introduced an additional step into the setup, requiring the user to install the module first.
   214  
   215  So in practice, we don't use the `.pyx` files directly (and they aren't required at runtime!). When the build process is over, the bindings are part of the Tyk binary and can be loaded and accessed from Go code.
   216  
   217  The bindings [declare an initialization function](tyk/gateway.h) that should be called after the Python interpreter is invoked, this will load the actual module and make it possible to import it using `import mymodule`. This is how [`gateway.pyx`](tyk/gateway.pyx) and its functions become available.
   218  
   219  ### Middleware and wrappers
   220  
   221  There are [quick wrappers](tyk/) for the HTTP request and session objects, written in Python, the idea is to provide an idiomatic way of writing middleware:
   222  
   223  ```python
   224  from tyk.decorators import *
   225  
   226  @Pre
   227  def AppendHeader(request, session, spec):
   228      request.add_header("custom_header", "custom_value")
   229      return request, session
   230  ```
   231  
   232  The decorators provide a simple way of indicating when it's the right moment to execute your handlers, a handler that is decorated with `Pre` will be called before any authentication occurs, `Post` will occur after authentication and will have access to the `session` object.
   233  You may find more information about Tyk middleware [here](https://tyk.io/docs/tyk-api-gateway-v1-9/javascript-plugins/middleware-scripting/).