Skip to main content

Tentaclio

CircleCI Documentation Status

Python library that simplifies:

  • Handling streams from different protocols such as file:, ftp:, sftp:, s3:, ...
  • Opening database connections.
  • Managing the credentials in distributed systems.

Main considerations in the design:

  • Easy to use: all streams are open via tentaclio.open, all database connections through tentaclio.db.
  • URLs are the basic resource locator and db connection string.
  • Automatic authentication for protected resources.
  • Extensible: you can add your own handlers for other schemes.
  • Pandas interaction.

Quick Examples.

Read and write streams.

import tentaclio
contents = "👋 🐙"

with tentaclio.open("ftp://localhost:2021/upload/file.txt", mode="w") as writer:
    writer.write(contents)

# Using boto3 authentication under the hood.
bucket = "s3://my-bucket/octopus/hello.txt"
with tentaclio.open(bucket) as reader:
    print(reader.read())

Copy streams

import tentaclio

tentaclio.copy("/home/constantine/data.csv", "sftp://constantine:tentacl3@sftp.octopus.energy/uploads/data.csv")

Delete resources

import tentaclio

tentaclio.remove("s3://my-bucket/octopus/the-9th-tentacle.txt")

List resources

import tentaclio

for entry in tentaclio.listdir("s3://my-bucket/path/to/dir"):
    print("Entry", entry)

Authenticated resources.

import os

import tentaclio

print("env ftp credentials", os.getenv("TENTACLIO__CONN__OCTOPUS_ENERGY_FTP"))
# This prints `sftp://constantine:tentacl3@sftp.octopus.energy/`

# Credentials get automatically injected.

with tentaclio.open("sftp://sftp.octopus.energy/uploads/data.csv") as reader:
    print(reader.read())

Database connections.

import os

import tentaclio

print("env TENTACLIO__CONN__DB", os.getenv("TENTACLIO__CONN__DB"))

# This prints `postgresql://octopus:tentacle@localhost:5444/example`

# hostname is a wildcard, the credentials get injected.
with tentaclio.db("postgresql://hostname/example") as pg:
    results = pg.query("select * from my_table")

Pandas interaction.

import pandas as pd  # 🐼🐼
import tentaclio  # 🐙

df = pd.DataFrame([[1, 2, 3], [10, 20, 30]], columns=["col_1", "col_2", "col_3"])

bucket = "s3://my-bucket/data/pandas.csv"

with tentaclio.open(bucket, mode="w") as writer:  # supports more pandas readers
    df.to_csv(writer, index=False)

with tentaclio.open(bucket) as reader:
    new_df = pd.read_csv(reader)

# another example: using pandas.DataFrame.to_sql() with tentaclio to upload
with tentaclio.db(
        connection_info,
        connect_args={'options': '-csearch_path=schema_name'}
    ) as client:
    df.to_sql(
        name='observations', # table name
        con=client.conn,
    )

Installation

You can install tentaclio with your favourite package manager:

pip install tentaclio
pipenv install tentaclio
uv install tentaclio

The installed version is available at runtime as tentaclio.__version__.

Developing.

Clone this repo and install uv.

In the Makefile you'll find some useful targets for linting, testing, etc.

For example:

make test

How to use

This is how to use tentaclio for your daily data ingestion and storing needs.

Streams

In order to open streams to load or store data the universal function is:

import tentaclio

with tentaclio.open("/path/to/my/file") as reader:
    contents = reader.read()

with tentaclio.open("s3://bucket/file", mode='w') as writer:
    writer.write(contents)

Allowed modes are r, w, rb, and wb. You can use t instead of b to indicate text streams, but that's the default.

In order to keep tentaclio as light as possible, it only includes file, ftp, sftp, http and https schemes by default. However, many more are easily available by installing extra packages:

Default:

  • /local/file
  • file:///local/file
  • ftp://path/to/file
  • sftp://path/to/file
  • http://host.com/path/to/resource
  • https://host.com/path/to/resource

tentaclio-s3

  • s3://bucket/file

tentaclio-gs

  • gs://bucket/file
  • gsc://bucket/file

tentaclio-gdrive

  • gdrive:/My Drive/file
  • googledrive:/My Drive/file

tentaclio-postgres

  • postgresql://host/database::table will allow you to write from a csv format into a database with the same column names (note that the table goes after :: :warning:).

You can add the credentials for any of the urls in order to access protected resources.

You can use these readers and writers with pandas functions like:

import pandas as pd
import tentaclio

with tentaclio.open("/path/to/my/file") as reader:
    df = pd.read_csv(reader)

[...]

with tentaclio.open("s3://my-bucket/path/to/my/file", mode='w') as writer:
    df.to_parquet(writer)

Readers, Writers and their closeable versions can be used anywhere expecting a file-like object; pandas or pickle are examples of such functions.

Notes on writing files for Spark, Presto, and similar downstream systems

The default behaviour for the open context manager in python is to create an empty file when opening it in writable mode. This can be annoying if the process that creates the data within the with clause yields empty dataframes and nothing gets written. This will make Spark and Presto panic.

To avoid this we can make the stream empty safe so the empty buffer won't be flushed if no writes have been performed so no empty file will be created.

with tio.make_empty_safe(tio.open("s3://my-bucket/file.parquet", mode="wb")) as writer:
    if not df.empty:
        df.to_parquet(writer)

File system like operations to resources

Listing resources

Some URL schemes allow listing resources in a pythonic way:

import tentaclio

for entry in tentaclio.listdir("s3://my-bucket/path/to/dir"):
    print("Entry", entry)

Whereas listdir might be convenient we also offer scandir, which returns a list of DirEntrys, and, walk. All functions follow as closely as possible their standard library definitions.

Database access

In order to open db connections you can use tentaclio.db and have instant access to postgres, sqlite, athena and mssql.

import tentaclio

[...]

query = "select 1";
with tentaclio.db(POSTGRES_TEST_URL) as client:
    result =client.query(query)
[...]

The supported db schemes are:

Default:

  • sqlite://
  • mssql://
    • Any other scheme supported by sqlalchemy.

tentaclio-postgres

  • postgresql://

tentaclio-athena

  • awsathena+rest://

tentaclio-databricks

  • databricks+thrift://

tentaclio-snowflake

  • snowflake://

Extras for databases

For postgres you can set the variable TENTACLIO__PG_APPLICATION_NAME and the value will be injected when connecting to the database.

Automatic credentials injection

  1. Configure credentials by using environmental variables prefixed with TENTACLIO__CONN__ (i.e. TENTACLIO__CONN__DATA_FTP=sftp://real_user:132ldsf@ftp.octopus.energy).

    This is the recommended way to configure credentials. One env var per credential is easier to manage in CI/CD and secret managers (no file to write, mount, or gitignore), and rotating or scoping a single credential doesn't require touching a shared YAML file. Prefer this over the credentials file below unless you have many credentials and want them all in one place.

  2. Open a stream:

with tentaclio.open("sftp://ftp.octopus.energy/file.csv") as reader:
    reader.read()

The credentials get injected into the url.

  1. Open a db client:
import tentaclio

with tentaclio.db("postgresql://hostname/my_data_base") as client:
    client.query("select 1")

Note that hostname in the url to be authenticated is a wildcard that will match any hostname. So authenticate("http://hostname/file.txt") will be injected to http://user:pass@octo.co/file.txt if the credential for http://user:pass@octo.co/ exists.

Different components of the URL are set differently:

  • Scheme and path will be set from the URL, and null if missing.
  • Username, password and hostname will be set from the stored credentials.
  • Port will be set from the stored credentials if it exists, otherwise from the URL.
  • Query will be set from the URL if it exists, otherwise from the stored credentials (so it can be overridden)

Credentials file

If you have many credentials, an alternative to setting one TENTACLIO__CONN__ env var per credential is a single YAML file. This is still fully supported, but individual TENTACLIO__CONN__ env vars are the recommended default — reach for a credentials file only when you need to manage a large number of secrets together.

The file looks like:

secrets:
    db_1: postgresql://user1:pass1@myhost.com/database_1
    db_2: mssql://user2:pass2@otherhost.com/database_2?driver=ODBC+Driver+17+for+SQL+Server
    ftp_server: ftp://fuser:fpass@ftp.myhost.com

And make it accessible to tentaclio by setting the environmental variable TENTACLIO__SECRETS_FILE. The actual name of each url is for traceability and has no effect in the functionality.

(Note that you may need to add ?driver={driver from /usr/local/etc/odbcinst.ini} for mssql database connection strings; see above example)

Alternatively you can run curl https://raw.githubusercontent.com/octopus-energy/tentaclio/main/extras/init_tentaclio.sh to create a secrets file in ~/.tentaclio.yml and automatically configure your environment.

Environment variables can be included in the credentials file by using ${ENV_VARIABLE} as it follows:

secrets:
    db: postgresql://${DB_USER}:${DB_PASS}@myhost.com/database

Tentaclio will search DB_USER and DB_PASS in the environment and will interpolate their values with the secrets file content.

Quick note on protocols structural subtyping.

In order to abstract concrete dependencies from the implementation of data related functions (or in any part of the system really) we use typed protocols. This allows a more flexible dependency injection than using subclassing or more complex approaches. This idea is heavily inspired by how this exact thing is done in go. Learn more about this principle in our tech blog.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tentaclio-1.5.1.tar.gz (235.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tentaclio-1.5.1-py3-none-any.whl (41.6 kB view details)

Uploaded Python 3

File details

Details for the file tentaclio-1.5.1.tar.gz.

File metadata

  • Download URL: tentaclio-1.5.1.tar.gz
  • Upload date:
  • Size: 235.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tentaclio-1.5.1.tar.gz
Algorithm Hash digest
SHA256 c68b60313f0a5cda87f95a7f95f8948abe03b280f189635a3d621ce9ba65f0a5
MD5 0cd139a77c5885fe1f60ab8ae7738f16
BLAKE2b-256 c48dcf92c6e3d85003e89b64bf2f2ae02caaf1dbe6df756d4ff547ff8e55b278

See more details on using hashes here.

File details

Details for the file tentaclio-1.5.1-py3-none-any.whl.

File metadata

  • Download URL: tentaclio-1.5.1-py3-none-any.whl
  • Upload date:
  • Size: 41.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for tentaclio-1.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3e3ada7cf9be1024a8b4e2d5a0938ff788b3ba6489b945336288d520d4753825
MD5 d646abb5d1313f89bd89d8029bbdc5b3
BLAKE2b-256 0de856bbaa159f5ba24685d34012e8baa7b27dda8db42f6fdf0fee8eb18beee9

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.5.1 This release

2 files

1.5.0

2 files

1.4.1

2 files

1.4.0

2 files

1.3.7

2 files

1.3.6

2 files

1.3.0

2 files

1.2.2

2 files

1.2.1

2 files

1.2.0

2 files

1.1.0

2 files

1.0.9

2 files

1.0.8

1 file

1.0.6

2 files

1.0.5

2 files

1.0.4

2 files

1.0.3

2 files

1.0.2

2 files

1.0.1

2 files

0.0.16

2 files

0.0.15

2 files

0.0.14

2 files

0.0.13

2 files

0.0.12

2 files

0.0.11

2 files

0.0.9

2 files

0.0.8

2 files

0.0.7

2 files

0.0.6

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page