Librería para gestión unificada de archivos en múltiples orígenes de almacenamiento
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tai_storage-0.1.16.tar.gz.
File metadata
- Download URL: tai_storage-0.1.16.tar.gz
- Upload date:
- Size: 70.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.4 CPython/3.11.0 Linux/6.17.0-1010-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fe40d0ec136c98eb82f3214f02f5e56ebb2b21b02d15d40805305b78aa32242
|
|
| MD5 |
dbffcdb316fc52c16ee76f9934e13a10
|
|
| BLAKE2b-256 |
173ded0e4c8837fd0f9cc905c569e7e8098f8a2ae1245fe6caea65716e736788
|
File details
Details for the file tai_storage-0.1.16-py3-none-any.whl.
File metadata
- Download URL: tai_storage-0.1.16-py3-none-any.whl
- Upload date:
- Size: 90.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.4 CPython/3.11.0 Linux/6.17.0-1010-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
257da0f9e97975eeb7978ca401347c698bad4ced846909b153330a5fe0b31825
|
|
| MD5 |
174123a72d417145dab64d4aed968e99
|
|
| BLAKE2b-256 |
92ecf83c19d48ee2d87808e48f39026bdd5620d3c39512d0fa65d846a17224ab
|