# Installing local Python modules into a Tesseract

## Context

Sometimes it might be necessary to bundle local Python modules into a Tesseract.

There are 2 ways to do this:

1. Make them a proper Python package with `pyproject.toml` and add the local path to the `tesseract_requirements.txt` file.
   Both absolute and relative paths work, but in case they are relative paths, they should be
   relative to the Tesseract's root folder (i.e., the one which contains the `tesseract_api.py` file).
2. Just put them as `.py` files next to `tesseract_api.py` and add them to `build_config.package_data` (see also [](packagedata.md)) in `tesseract_config.yaml` to make sure they're being included in container builds.

## Example Tesseract

Here is an example Tesseract that highlights both ways to include dependencies.

```{literalinclude} ../../../../examples/localpackage/tesseract_api.py
:language: python
```

The custom package `helloworld` is shipped with a file `helloworld.py` and a `pyproject.toml` that ensures it can be built as a Python package.

```{literalinclude} ../../../../examples/localpackage/helloworld/helloworld.py
:language: python
```

```{literalinclude} ../../../../examples/localpackage/helloworld/pyproject.toml
:language: toml
```

Then, it can be added as a local dependency to `tesseract_requirements.txt` by passing a relative path:

```{literalinclude} ../../../../examples/localpackage/tesseract_requirements.txt
:language: text
```

The local module `goodbyeworld.py` is just a single Python file:

```{literalinclude} ../../../../examples/localpackage/goodbyeworld.py
:language: python
```

To ensure it gets added to built Tesseracts, we have to register it as `package_data`:

```{literalinclude} ../../../../examples/localpackage/tesseract_config.yaml
:language: yaml
```

Now we are ready to build the Tesseract. This Tesseract will accept a name like "Tessie" as an input and return a message:

```bash
$ tesseract build examples/localpackage
$ tesseract run localpackage apply '{"inputs": {"name": "Tessie"}}'
{"message":"Hello Tessie!\nGoodbye Tessie!"}
```

This confirms that both custom dependencies are available to the Tesseract container.

To run the Tesseract without containerization, we have to make sure that `helloworld` is installed into our dev environment:

```bash
$ pip install examples/localpackage/helloworld
$ TESSERACT_API_PATH=examples/localpackage/tesseract_api.py tesseract-runtime apply '{"inputs": {"name": "Tessie"}}'
{"message":"Hello Tessie!\nGoodbye Tessie!"}
```

## Advanced pattern: injecting private dependencies as local wheels

In case a Tesseract depends on private packages that are not accessible from the public Python package index (PyPI), injecting them as local files can be a useful way to side-step authentication issues.

This is especially powerful in conjunction with `pip download` (e.g. from a private PyPI registry) to obtain a pre-built wheel:

```bash
$ pip download cowsay==6.1
Collecting cowsay==6.1
  Obtaining dependency information for cowsay==6.1 from https://files.pythonhosted.org/packages/f1/13/63c0a02c44024ee16f664e0b36eefeb22d54e93531630bd99e237986f534/cowsay-6.1-py3-none-any.whl.metadata
  Downloading cowsay-6.1-py3-none-any.whl.metadata (5.6 kB)
Downloading cowsay-6.1-py3-none-any.whl (25 kB)
Saved ./cowsay-6.1-py3-none-any.whl
Successfully downloaded cowsay
```

We can then specify it as a local dependency in `tesseract_requirements.txt` by adding the following line:

```
./cowsay-6.1-py3-none-any.whl
```

Finally, let's build the Tesseract, and verify it works

```bash
$ tesseract build mytess
 [i] Building image ...
 [i] Built image sha256:7d024, ['mytess:latest']

$ tesseract run mytess apply '{"inputs": {"message": "Hello, World!"}}'
{"out":"  _____________\n| Hello, World! |\n  =============\n             \\\n              \\\n                ^__^\n                (oo)\\_______\n                (__)\\       )\\/\\\n                    ||----w |\n                    ||     ||"}
```
