TAI-Storage
Librería para gestión unificada de archivos en múltiples orígenes de almacenamiento
📋 Descripción
tai-storage proporciona una interfaz única y consistente para manejar archivos almacenados en diferentes orígenes:
- 💾 Sistema de archivos local ✅
- ☁️ Azure Blob Storage (próximamente)
- 🔐 Servidores SFTP/FTP (próximamente)
- 📁 SharePoint Online ✅ (Microsoft Graph API + Azure AD)
- Y más...
Características Principales
✨ Interfaz Única: Mismo código funciona con cualquier origen
🔄 Modo Desarrollo: Caché local automático para optimizar tiempos de desarrollo
🔒 Gestión Segura de Secretos: Integración con variables de entorno
📊 Logging Avanzado: Integración con tai-alphi
🎯 Type Hints: Soporte completo para IDEs y validación de tipos
🧪 Testeable: Fácil creación de mocks para testing
🔬 Perfilado de Datos: Análisis estadístico de DataFrames vía df.profile
🚀 Instalación
Con Poetry (recomendado)
# Instalación básica
poetry add tai-storage
# Con soporte para Azure
poetry add tai-storage[azure]
# Con soporte para SFTP
poetry add tai-storage[sftp]
# Con soporte para SharePoint
poetry add tai-storage[sharepoint]
# Con módulo de datos (pandas, perfilado, etc.)
poetry add tai-storage[data]
# Con todos los orígenes y módulos
poetry add tai-storage[all]
Con Pip
# Instalación básica
pip install tai-storage
# Con soporte para Azure
pip install tai-storage[azure]
# Con soporte para SharePoint
pip install tai-storage[sharepoint]
# Con módulo de datos (pandas, perfilado, etc.)
pip install tai-storage[data]
# Con todos los orígenes y módulos
pip install tai-storage[all]
📖 Uso Básico
from tai_storage import LocalOrigin
# Crear origen local
origin = LocalOrigin(root_path='/data')
# Obtener carpeta y archivo
folder = origin.get_folder('reports')
file = folder.get_file('report.csv')
# Leer archivo
data = file.read()
text = file.read_text()
# Listar archivos
files = folder.list_files(pattern=r'.*\.csv$')
Nota: Implementaciones disponibles: Local ✅, SharePoint ✅ y Azure Blob Storage ✅. StorageFactory ✅ disponible.
🏗️ Arquitectura
La librería se basa en tres clases abstractas principales:
1. Origin (Sistema de Almacenamiento)
Representa el origen de datos (Azure, SFTP, local, etc.)
class Origin(ABC):
def connect()
def disconnect()
def get_folder(path) -> Folder
def create_folder(path) -> Folder
def folder_exists(path) -> bool
# ...
2. Folder (Carpeta/Contenedor)
Representa una carpeta dentro de un origen
class Folder(ABC):
def list_files(pattern, recursive) -> List[str]
def get_file(filename) -> File
def upload_file(source, destination) -> File
def delete_file(filename)
# ...
3. File (Archivo)
Representa un archivo individual
class File(ABC):
def read() -> bytes
def read_text() -> str
def write(data: bytes)
def delete()
def copy_to(destination: File)
# Properties: name, extension, size, created_time, modified_time
# ...
🎯 Ejemplos de Uso
Usando StorageFactory (Recomendado)
La forma más sencilla de trabajar con tai-storage es usando StorageFactory:
from tai_storage import StorageFactory
# Sistema Local
local = StorageFactory.create_local(root_path='/data')
# Azure Blob Storage con parámetros
azure = StorageFactory.create_azure(
account_name='myaccount',
account_key='mykey123'
)
# O usando variables de entorno
# export AZURE_STORAGE_ACCOUNT_NAME=myaccount
# export AZURE_STORAGE_ACCOUNT_KEY=mykey123
azure = StorageFactory.create_azure() # ← Lee desde env vars
# SharePoint Online
sharepoint = StorageFactory.create_sharepoint(
tenant_id='your-tenant-id',
client_id='your-client-id',
client_secret='your-secret',
site_name='Documents',
hostname='contoso.sharepoint.com'
)
Trabajar con Archivos
# Usar cualquier origin (local, Azure, SharePoint...)
origin = StorageFactory.create_local(root_path='/data')
# Obtener carpeta y archivo
folder = origin.get_folder('documents')
file = folder.get_file('report.pdf')
# Verificar existencia y leer
if file.exists():
data = file.read()
print(f"Tamaño: {file.size} bytes")
print(f"Modificado: {file.modified_time}")
# Listar archivos con patrón
pdf_files = folder.list_files(pattern=r'.*\.pdf$')
for filename in pdf_files:
print(f"Encontrado: {filename}")
Azure Blob Storage
from tai_storage import StorageFactory
# Opción 1: Connection string
azure = StorageFactory.create_azure(
connection_string='DefaultEndpointsProtocol=https;...'
)
# Opción 2: Account name + key
azure = StorageFactory.create_azure(
account_name='mystorageaccount',
account_key='mykey123...'
)
# Opción 3: Variables de entorno
# export AZURE_STORAGE_CONNECTION_STRING='...'
azure = StorageFactory.create_azure()
# Trabajar con containers (similar a folders)
container = azure.get_folder('documents')
files = container.list_files()
for filename in files:
file = container.get_file(filename)
print(f"{filename}: {file.size} bytes")
SharePoint Online
from tai_storage import StorageFactory
# Crear origin de SharePoint
sp = StorageFactory.create_sharepoint(
tenant_id='your-tenant-id',
client_id='your-client-id',
client_secret='your-secret',
site_name='Documents',
hostname='contoso.sharepoint.com'
)
# Trabajar con carpetas y archivos
folder = sp.get_folder('Shared Documents/Reports')
files = folder.list_files(pattern=r'.*\.xlsx$')
for filename in files:
file = folder.get_file(filename)
data = file.read()
print(f"Procesado: {filename}")
Copiar entre Orígenes
from tai_storage import StorageFactory
# Crear orígenes
local = StorageFactory.create_local(root_path='/data')
azure = StorageFactory.create_azure()
# Copiar de local a Azure
source_file = local.get_folder('input').get_file('data.csv')
dest_folder = azure.get_folder('backup')
# Opción 1: Usando upload_file
data = source_file.read()
dest_folder.upload_file(data, 'data.csv')
# Opción 2: Usando copy_to
dest_file = dest_folder.get_file('data.csv')
source_file.copy_to(dest_file)
Context Manager
from tai_storage import StorageFactory
# Auto connect/disconnect con context manager
with StorageFactory.create_local(root_path='/data') as origin:
folder = origin.get_folder('documents')
files = folder.list_files()
print(f"Encontrados {len(files)} archivos")
# Automáticamente desconectado al salir del contexto
# También funciona con SharePoint y Azure
with StorageFactory.create_azure() as azure:
container = azure.get_folder('backups')
# ... trabajar con archivos
# Desconectado automáticamente
Filtrar Archivos por Fecha
from datetime import datetime, timedelta
from tai_storage import StorageFactory
origin = StorageFactory.create_local(root_path='/data')
folder = origin.get_folder('logs')
# Archivos modificados en los últimos 7 días
since = datetime.now() - timedelta(days=7)
recent_files = folder.filter_files_by_date(modified_after=since)
for filename in recent_files:
file = folder.get_file(filename)
print(f"{filename}: modificado {file.modified_time}")
🔑 Configurar SharePoint con Azure AD
Para usar SharePoint necesitas registrar una aplicación en Azure AD:
1. Registrar Aplicación en Azure Portal
- Ve a Azure Portal → Azure Active Directory
- En el menú lateral: App registrations → New registration
- Configura:
- Name:
tai-storage-app(o el nombre que prefieras) - Supported account types:
Accounts in this organizational directory only - Redirect URI: Déjalo vacío (no es necesario para aplicaciones daemon)
- Name:
- Click en Register
- Copia el Application (client) ID → Este es tu
client_id - En la página Overview, copia el Directory (tenant) ID → Este es tu
tenant_id
2. Crear Client Secret
- En tu aplicación, ve a Certificates & secrets
- Click en New client secret
- Configura:
- Description:
tai-storage-secret - Expires: Selecciona duración apropiada (ej: 24 meses)
- Description:
- Click en Add
- ⚠️ IMPORTANTE: Copia el Value inmediatamente → Este es tu
client_secret- ⚠️ Solo se muestra una vez, no podrás volver a verlo
3. Configurar Permisos de Microsoft Graph API
- En tu aplicación, ve a API permissions
- Click en Add a permission → Microsoft Graph → Application permissions
- Busca y añade estos permisos:
Sites.Selected- Acceso solo a sitios específicos (recomendado)
- Click en Add permissions
- ⚠️ CRÍTICO: Click en Grant admin consent for [Tu Organización]
- ⚠️ Necesitas permisos de administrador para este paso
- Sin esto, la aplicación NO funcionará
4. Otorgar Acceso al Sitio Específico
Ahora debes dar permisos explícitos al sitio concreto usando Microsoft Graph API con PowerShell.
💡 Script disponible: Puedes usar el script completo disponible en scripts/permisos_app_sharepoint.ps1
# 0. Limpiar sesión anterior (opcional pero recomendado)
Disconnect-MgGraph -ErrorAction SilentlyContinue
# 1. Instalar módulo si no lo tienes
Install-Module Microsoft.Graph -Scope CurrentUser
# 2. Conectar con permisos de admin
Connect-MgGraph -Scopes "Sites.FullControl.All" -NoWelcome
# 3. Configuración - REEMPLAZA CON TUS VALORES
$hostName = "<YOUR_TENANT>.sharepoint.com" # ej: triplealphain.sharepoint.com
$siteName = "<SITE_NAME>" # ej: TeamSite, Demeter
$sitePath = "/sites/$siteName"
$appId = "TU-CLIENT-ID-AQUI" # Tu Application (client) ID
$appName = "tai-storage-app" # Nombre de tu aplicación
Write-Host "Buscando sitio en: $hostName$sitePath ..." -ForegroundColor Cyan
try {
# 4. Obtener el sitio usando la API directa
$apiUrl = "https://graph.microsoft.com/v1.0/sites/$($hostName):$($sitePath)"
$site = Invoke-MgGraphRequest -Method GET -Uri $apiUrl
Write-Host "¡EXITO! Sitio encontrado." -ForegroundColor Green
Write-Host "Nombre: $($site.displayName)"
Write-Host "ID: $($site.id)" -ForegroundColor Gray
# 5. Asignar permisos write (lectura y escritura)
$params = @{
roles = @("write") # Opciones: read, write, owner
grantedToIdentities = @(
@{
application = @{
id = $appId
displayName = $appName
}
}
)
}
Invoke-MgGraphRequest -Method POST `
-Uri "https://graph.microsoft.com/v1.0/sites/$($site.id)/permissions" `
-Body ($params | ConvertTo-Json -Depth 5) `
-ContentType "application/json"
Write-Host "Permisos asignados correctamente." -ForegroundColor Green
} catch {
# Manejo de errores
Write-Host "ERROR AL BUSCAR EL SITIO:" -ForegroundColor Red
if ($_.Exception.Message -match "Not Found" -or $_.ErrorDetails.Message -match "Not Found") {
Write-Host "El sitio '$sitePath' NO EXISTE." -ForegroundColor Yellow
Write-Host "Verifica el nombre del sitio en SharePoint."
} else {
Write-Host $_.Exception.Message -ForegroundColor Yellow
if ($_.Exception.InnerException) {
Write-Host "Detalle: $($_.Exception.InnerException.Message)" -ForegroundColor DarkYellow
}
}
}
5. Obtener Información del Sitio de SharePoint
- Ve a tu sitio de SharePoint:
https://<YOUR_TENANT>.sharepoint.com/sites/<SITE_NAME> - De la URL extrae:
- hostname:
<YOUR_TENANT>.sharepoint.com(ej:contoso.sharepoint.com,triplealphain.sharepoint.com) - site_name:
<SITE_NAME>(la parte después de/sites/, ej:TeamSite,ProjectSite)
- hostname:
6. Configurar Variables de Entorno
# En tu sistema o .env
export SHAREPOINT_TENANT_ID="12345678-1234-1234-1234-123456789abc"
export SHAREPOINT_CLIENT_ID="87654321-4321-4321-4321-cba987654321"
export SHAREPOINT_CLIENT_SECRET="tu~secret~super~secreto"
export SHAREPOINT_SITE_NAME="<SITE_NAME>" # ej: TeamSite, ProjectSite
export SHAREPOINT_HOSTNAME="<YOUR_TENANT>.sharepoint.com" # ej: contoso.sharepoint.com
7. Usar en tu Código
from tai_storage import SharePointOrigin
from tai_storage.secrets import SecretManager
origin = SharePointOrigin(
tenant_id=SecretManager.get_secret(env_var="SHAREPOINT_TENANT_ID"),
client_id=SecretManager.get_secret(env_var="SHAREPOINT_CLIENT_ID"),
client_secret=SecretManager.get_secret(env_var="SHAREPOINT_CLIENT_SECRET"),
site_name=SecretManager.get_secret(env_var="SHAREPOINT_SITE_NAME"),
hostname=SecretManager.get_secret(env_var="SHAREPOINT_HOSTNAME")
)
origin.connect()
# ... trabajar con SharePoint
origin.disconnect()
📋 Checklist de Verificación
- Aplicación registrada en Azure AD
- Application (client) ID copiado →
client_id - Directory (tenant) ID copiado →
tenant_id - Client secret creado y copiado →
client_secret - Permiso
Sites.Selectedañadido (NOSites.ReadWrite.All) - Admin consent otorgado (paso crítico)
- Permisos específicos del sitio otorgados (PowerShell/API - paso crítico)
- Hostname y site_name del sitio de SharePoint identificados
- Variables de entorno configuradas
-
pip install tai-storage[sharepoint]ejecutado (omsalyrequestsinstalados)
🔒 Ventajas de usar Sites.Selected
- ✅ Seguridad mejorada: La app solo accede al sitio específico que necesita
- ✅ Principio de mínimo privilegio: No tiene acceso a otros sitios de la organización
- ✅ Auditoría clara: Fácil ver qué apps tienen acceso a qué sitios
- ✅ Revocación simple: Puedes revocar acceso sin eliminar la app
- ⚠️ Requiere configuración adicional: Necesitas otorgar permisos explícitamente con PowerShell/API
⚠️ Errores Comunes
- "Insufficient privileges": No se otorgó admin consent o no se otorgaron permisos al sitio específico
- "Access denied": Usaste
Sites.Selectedpero no otorgaste permisos al sitio con PowerShell/API - "Application not found": Tenant ID o Client ID incorrectos
- "Invalid client secret": El secret expiró o se copió incorrectamente
- "Site not found": Hostname o site_name incorrectos
- ImportError msal: Falta
pip install msal requests
🔄 Para otorgar acceso a múltiples sitios
Si necesitas acceso a varios sitios específicos, repite el paso 4 para cada sitio:
# Sitio 1
$site1 = Get-MgSite -Search "<SITE_NAME_1>" # ej: TeamSite
Invoke-MgGraphRequest -Method POST `
-Uri "https://graph.microsoft.com/v1.0/sites/$($site1.Id)/permissions" `
-Body $params
# Sitio 2
$site2 = Get-MgSite -Search "<SITE_NAME_2>" # ej: ProjectSite
Invoke-MgGraphRequest -Method POST `
-Uri "https://graph.microsoft.com/v1.0/sites/$($site2.Id)/permissions" `
-Body $params
📖 Referencias
Filtrar Archivos por Fecha
from datetime import datetime, timedelta
folder = origin.get_folder('logs')
# Archivos de los últimos 7 días
since = datetime.now() - timedelta(days=7)
recent_files = folder.filter_files_by_date(since=since)
for file_meta in recent_files:
print(f"{file_meta['name']}: {file_meta['modified_time']}")
🔬 Módulo de Datos
Requiere instalación con el extra [data]:
pip install tai-storage[data]
Leer archivos como DataFrame
Cualquier File expone el método get_data() que detecta automáticamente
el formato (CSV, Excel, Parquet, JSON) e infiere tipos de datos:
from tai_storage import LocalOrigin
origin = LocalOrigin(root_path='/data')
file = origin.get_folder('reports').get_file('ventas.xlsx')
# Leer como DataFrame con inferencia de tipos
df = file.get_data().to_dataframe(sheet_name='Ventas', infer_types=True)
# O usar los métodos específicos por formato
df = file.get_data().to_csv(sep=';')
df = file.get_data().to_excel(sheet_name='Ventas')
df = file.get_data().to_parquet()
Accessor df.profile
Al importar tai_storage.data se registra automáticamente el accessor
.profile en todos los DataFrames de pandas:
import pandas as pd
import tai_storage.data # activa df.profile
df = pd.read_csv('ventas.csv')
# Nombrar el dataset (opcional, mejora los reportes)
df.profile.data_name = 'ventas_2024'
# Perfil de una columna específica
col = df.profile.get('precio')
print(col.type) # 'numeric'
print(col.num_unicos) # nº de valores únicos
print(col.relative_vacios) # fracción de nulos (0.0 – 1.0)
print(col.unicos) # lista de valores únicos ordenados
# Acceso también vía []
col = df.profile['fecha']
# Generar y mostrar todos los perfiles
df.profile.generate() # genera todos (lazy, solo columnas nuevas)
df.profile.show() # imprime por consola (auto-genera si falta)
# Análisis de correlación entre columnas
df.profile.show_correlation_matrix() # nulos = coincidencia
df.profile.show_correlation_matrix(include_na=False)
# Detectar columnas funcionalmente determinadas por una clave
df.profile.show_duplicates_by_key('cliente_id')
df.profile.show_duplicates_by_key(['pais', 'provincia'])
Integridad referencial entre columnas
col_a = df_pedidos.profile['cliente_id']
col_b = df_clientes.profile['id']
col_a.referencial_integrity_with(col_b)
# ✅ SI. Considera: "clientes[id]" -- one to many -- "pedidos[cliente_id]"
Perfilado programático
from tai_storage.data import Profiles, ColumnProfile
profiles = Profiles(df, data_name='ventas')
profiles.generate()
col = profiles.get('precio')
print(col.type) # 'numeric'
print(col.num_vacios) # int
print(col.relative_vacios) # float
print(col.distribucion) # list[(valor, frecuencia%)]
Nota: Los perfiles se cachean por instancia de DataFrame. Llamar
df.profile.get('col')varias veces devuelve siempre el mismo objeto.
⚙️ Configuración
Logging con tai-alphi
from tai_storage import configure_logger
# Configurar logging
configure_logger(
log_level='DEBUG', # DEBUG, INFO, WARN, ERROR, CRIT
display_info=['asctime', 'levelname', 'funcName'],
time_format='%H:%M:%S'
)
# Para acceder directamente al logger desde otros módulos:
from tai_alphi import Alphi
logger = Alphi.get_logger_by_name('tai-storage')
logger.info('Mensaje personalizado')
Variables de Entorno
StorageFactory puede obtener credenciales de variables de entorno:
# Local
export STORAGE_LOCAL_ROOT_PATH="/data/files"
# Azure Blob Storage (Opción 1: Connection String)
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net"
# Azure Blob Storage (Opción 2: Account + Key)
export AZURE_STORAGE_ACCOUNT_NAME="mystorageaccount"
export AZURE_STORAGE_ACCOUNT_KEY="mykey123..."
# SharePoint Online
export SHAREPOINT_TENANT_ID="12345678-1234-1234-1234-123456789abc"
export SHAREPOINT_CLIENT_ID="87654321-4321-4321-4321-cba987654321"
export SHAREPOINT_CLIENT_SECRET="tu~secret~super~secreto"
export SHAREPOINT_SITE_NAME="TeamSite" # Nombre del sitio
export SHAREPOINT_HOSTNAME="contoso.sharepoint.com" # Tu tenant
Luego puedes crear origins sin parámetros:
from tai_storage import StorageFactory
# Lee credenciales de variables de entorno
local = StorageFactory.create_local()
azure = StorageFactory.create_azure()
sharepoint = StorageFactory.create_sharepoint()
Parámetros vs Variables de Entorno
StorageFactory prioriza parámetros directos sobre variables de entorno:
# 1. Parámetros directos (prioridad alta)
origin = StorageFactory.create_azure(
account_name='myaccount',
account_key='mykey'
)
# 2. Variables de entorno (fallback)
# export AZURE_STORAGE_ACCOUNT_NAME=myaccount
origin = StorageFactory.create_azure() # Usa env vars
# 3. Mix (parámetros + env vars)
# export AZURE_STORAGE_ACCOUNT_KEY=mykey
origin = StorageFactory.create_azure(
account_name='myaccount' # key viene de env var
)
🧪 Testing
from tai_storage import LocalOrigin
import tempfile
def test_file_operations():
with tempfile.TemporaryDirectory() as tmpdir:
origin = LocalOrigin(root_path=tmpdir)
folder = origin.create_folder('test')
# Test upload
folder.upload_file(b'test data', 'test.txt')
# Test read
file = folder.get_file('test.txt')
assert file.read() == b'test data'
# Test delete
file.delete()
assert not file.exists()
Ejecutar tests:
# Tests de fundamentos (Fase 1)
python -m pytest tests/test_fase1.py -v
# Tests de implementación local (Fase 2)
python -m pytest tests/test_fase2_local.py -v
# Tests de SharePoint (requiere credenciales configuradas)
python -m pytest tests/test_sharepoint.py -v
# Todos los tests
python -m pytest tests/ -v
# Ejemplos funcionales
python examples_local.py
python examples_sharepoint.py # Requiere credenciales
📦 Estructura del Proyecto
tai_storage/
├── base/ # Clases abstractas
│ ├── origin.py
│ ├── folder.py
│ └── file.py
├── implementations/ # Implementaciones
│ ├── local/ # ✅ Sistema de archivos local
│ │ ├── origin.py
│ │ ├── folder.py
│ │ └── file.py
│ ├── sharepoint/ # ✅ SharePoint Online (Graph API)
│ │ ├── origin.py
│ │ ├── folder.py
│ │ └── file.py
│ ├── azure/ # ✅ Azure Blob Storage
│ │ ├── origin.py
│ │ ├── folder.py
│ │ └── file.py
│ ├── sftp/ # 🔜 SFTP
│ └── ftp/ # 🔜 FTP
├── cache/ # Sistema de caché para modo DEV
│ ├── cached_origin.py
│ ├── cached_folder.py
│ └── cached_file.py
├── data/ # Módulo de datos (extra [data])
│ ├── readers.py # CSV, Excel, Parquet, JSON readers
│ ├── file_reader.py # FileDataReader (detección automática)
│ ├── types.py # TypeInferencer, infer_and_convert_dataframe
│ ├── transformers.py # normalizar_texto y otras transformaciones
│ ├── profile.py # ColumnProfile, Profiles, CorrelationMatrix
│ └── accessor.py # Accessor df.profile para pandas
├── factory.py # StorageFactory
├── secrets.py # SecretManager
├── exceptions.py # Excepciones personalizadas
├── logger.py # Configuración de logging
└── utils.py # Utilidades
🗺️ Roadmap
- Fase 1: Fundamentos (clases base, excepciones, secretos) ✅
- Fase 2: Implementación Local ✅
- Fase 2.5: Implementación SharePoint (Microsoft Graph API + MSAL) ✅
- Fase 3: Sistema de Caché (Modo DEV)
- Fase 4: Factory y Secrets
- Fase 5: Implementación Azure Blob Storage
- Fase 6: Implementaciones SFTP, FTP
🤝 Contribución
Las contribuciones son bienvenidas. Por favor:
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/amazing) - Commit tus cambios (
git commit -m 'Add amazing feature') - Push a la rama (
git push origin feature/amazing) - Abre un Pull Request
📄 Licencia
MIT License - ver archivo LICENSE para más detalles
🆘 Soporte
Para reportar bugs o solicitar features:
- Abre un issue en GitHub
- Contacta al equipo de desarrollo
👥 Autores
- Mateo Saez Mata - msaez@triplealpha.in
Release files for tai-storage 0.1.16
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tai_storage-0.1.16.tar.gz | 70.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tai_storage-0.1.16-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 161.1 kB
Release files / tai_storage-0.1.16.tar.gz
| Download URL | tai_storage-0.1.16.tar.gz |
|---|---|
| Size | 70.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4fe40d0ec136c98eb82f3214f02f5e56ebb2b21b02d15d40805305b78aa32242
|
|
BLAKE2b-256 checksum How to use checksums |
173ded0e4c8837fd0f9cc905c569e7e8098f8a2ae1245fe6caea65716e736788
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.4 CPython/3.11.0 Linux/6.17.0-1010-azure
|
Release files / tai_storage-0.1.16-py3-none-any.whl
| Download URL | tai_storage-0.1.16-py3-none-any.whl |
|---|---|
| Size | 90.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
257da0f9e97975eeb7978ca401347c698bad4ced846909b153330a5fe0b31825
|
|
BLAKE2b-256 checksum How to use checksums |
92ecf83c19d48ee2d87808e48f39026bdd5620d3c39512d0fa65d846a17224ab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.3.4 CPython/3.11.0 Linux/6.17.0-1010-azure
|