This release is a pre-release and may not be stable for production use.
🌽💪 Cornflex 🌽💪
A lightweight reader for SFTP servers and JSON HTTPS APIs, built on paramiko, requests and polars. Pulls files and API records down as strings, Polars DataFrames, or raw text.
Installation
pip install cornflex
SFTP
from cornflex import SFTPReader
Connect with password
reader = SFTPReader(
hostname="sftp.example.com",
username="myuser",
password="mypassword",
)
Connect with PEM key
reader = SFTPReader(
hostname="sftp.example.com",
username="myuser",
pem_file="/path/to/key.pem",
)
Methods
get_files(remote_path, file_pattern)
Lists files in a remote directory. Supports glob-style patterns.
with reader:
reader.get_files(remote_path="/data", file_pattern="*.csv")
# ["orders_2024.csv", "users_2024.csv"]
get_csv_file(file_name, remote_path, column_names)
Fetches a CSV and returns it as a Polars DataFrame.
reader.connect()
df = reader.get_csv_file("orders.csv", remote_path="/data")
# Override column names (useful when file has no header)
df = reader.get_csv_file("orders.csv", remote_path="/data", column_names=["id", "amount", "date"])
reader.close()
get_xml_file_to_string(file_name, remote_path)
Fetches an XML file and returns it as a string.
reader.connect()
xml = reader.get_xml_file_to_string("feed.xml", remote_path="/exports")
reader.close()
file_to_string(file_name, remote_path, encoding)
Fetches any file as a string. Auto-detects encoding via chardet if not specified.
reader.connect()
content = reader.file_to_string("report.txt", remote_path="/reports")
# Force encoding
content = reader.file_to_string("report.txt", encoding="latin-1")
reader.close()
API
from cornflex import APIReader
with APIReader(
base_url="https://api.example.com/v1",
pfx_file="/path/to/client.pfx", # optional client certificate
pfx_password="secret",
verify=True, # False skips TLS verification, or a CA bundle path
) as api:
data = api.get_json("orders")
records = api.get_records("orders", records_path="data.items", max_pages=5)
df = api.get_dataframe("orders", max_pages=5)
# POST a JSON body and get the parsed JSON back
result = api.post_json("persons-list-request", json={"ids": batch})
- Errors (status 400 and above) include the first 300 characters of the response body. Set
include_body_in_errors=Falsewhen responses may echo sensitive input; the exception then holds onlyMETHOD url returned status. get_records/get_dataframeare GET-only; for POST endpoints usepost_jsonand build the records yourself.get_records/get_dataframeauto-detect the largest array of objects unlessrecords_path(dot-path,"$"for the root) is given.- Pagination is followed up to
max_pagesvia aLinkheader or anext/links.nextfield. Requests never leave the host ofbase_url, so the client certificate is not sent elsewhere.
Analysing an API
from cornflex import APIReader, describe_api, find_api_docs, infer_schema
with APIReader(base_url="https://api.example.com/v1") as api:
describe_api(api, "orders", table="orders") # prints structure, pagination hints, schema and CREATE TABLE
find_api_docs(api) # (url, spec) of an OpenAPI/Swagger doc, or None
schema = infer_schema(api.get_records("orders"))
Reading an OpenAPI spec
find_api_docs returns the spec; the describe_* helpers turn it into readable text (OpenAPI 3 only).
from cornflex import (
APIReader, find_api_docs, describe_openapi, find_endpoints, describe_endpoint, describe_schema,
)
with APIReader(base_url="https://api.example.com") as api:
url, spec = find_api_docs(api) # or: spec = api.get_json("/v3/api-docs")
print(describe_openapi(spec)) # title, servers, endpoints grouped by tag
print(find_endpoints(spec, "person")) # search path / summary / operationId
print(describe_endpoint(spec, "/v1/persons", "post")) # parameters, request body, responses
print(describe_schema(spec, "Person")) # one model as a field tree (* = required)
list_endpoints(spec) returns the same endpoints as a list of dicts. describe_endpoint and describe_schema raise ValueError for an unknown path, method or schema name.
Notes
- All methods need an open connection: use
with reader:or callconnect()andclose(). - Either
passwordorpem_filemust be provided — not both, not neither. get_csv_fileassumes UTF-8. Usefile_to_stringfor other encodings.
Dependencies
| Package | Purpose |
|---|---|
paramiko |
SSH/SFTP connection |
requests |
HTTPS API calls |
cryptography |
Reading .pfx client certificates |
polars |
DataFrame output |
chardet |
Encoding detection |
Metadata
Release files for cornflex 2.0.0rc1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cornflex-2.0.0rc1.tar.gz | 69.0 kB | Without provenance | Details for cornflex-2.0.0rc1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| cornflex-2.0.0rc1-py3-none-any.whl | Python 3 | none | any | Details for cornflex-2.0.0rc1-py3-none-any.whl |
Total release size: 86.5 kB
Release files / cornflex-2.0.0rc1.tar.gz
| Download URL | cornflex-2.0.0rc1.tar.gz |
|---|---|
| Size | 69.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
34b1b222c5ebfc39f7ac227a793b4063d5ffc6a19ef10ed0c0061e270f25bb8d
|
|
BLAKE2b-256 checksum How to use checksums |
eb028fcfab6f4e9d180bb2a0c25b950ac1ea52a53ab6d4b83165fd8af85f36ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|
Release files / cornflex-2.0.0rc1-py3-none-any.whl
| Download URL | cornflex-2.0.0rc1-py3-none-any.whl |
|---|---|
| Size | 17.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f4090eb9e7984b8ed41504d36918c1629a6470e2b5fd33bcc3299dfe123b76d
|
|
BLAKE2b-256 checksum How to use checksums |
80de413fdd856615f86d4c4cc5f7839f94945f1db52b7ff174c08a412ae56f0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.8
|
Security for cornflex 2.0.0rc1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.