Skip to main content

ironflock

Coverage

About

With this library you can publish data from your apps on your IoT edge hardware to the fleet data storage of the IronFlock devops platform. When this library is used on a certain device the library automatically uses the private messaging realm (Unified Name Space) of the device's fleet and the data is collected in the respective fleet database.

So if you use the library in your app, the data collection will always be private to the app user's fleet.

For more information on the IronFlock IoT Devops Platform for engineers and developers visit our IronFlock home page.

Requirements

  • Python 3.8 or higher

Installation

Install from PyPI:

pip install ironflock

Usage

import asyncio
from ironflock import IronFlock

# create an IronFlock instance to connect to the IronFlock platform data infrastructure.
# The IronFlock instance handles authentication when run on a device registered in IronFlock.
ironflock = IronFlock()

async def main():
    while True:
        # publish an event (if connection is not established the publish is skipped)
        publication = await ironflock.publish("test.publish.example", {"temperature": 20})
        print(publication)
        await asyncio.sleep(3)


if __name__ == "__main__":
    ironflock = IronFlock(mainFunc=main)
    ironflock.run()

Options

The IronFlock __init__ function can be configured with the following options:

{
    serial_number: string;
}

serial_number: Used to set the serial_number of the device if the DEVICE_SERIAL_NUMBER environment variable does not exist. It can also be used if the user wishes to authenticate as another device.

API Reference

publish(topic, *args, **kwargs)

Publishes an event to a topic on the IronFlock message router.

publication = await ironflock.publish("com.myapp.mytopic", {"temperature": 20})
Parameter Type Description
topic str The URI of the topic to publish to
*args positional Payload arguments
**kwargs keyword Payload keyword arguments

Returns: Publication — The publication object (an acknowledged publish receipt).

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the publish fails (e.g. not connected).


publish_to_table(tablename, *args, **kwargs)

Convenience function to publish data to a fleet table in the IronFlock platform. Automatically constructs the correct topic using the SWARM_KEY and APP_KEY environment variables.

await ironflock.publish_to_table("sensordata", {"temperature": 22.5, "humidity": 60})
Parameter Type Description
tablename str The name of the table, e.g. "sensordata"
*args positional Row data to publish
**kwargs keyword Row data as keyword arguments

Returns: Publication — The publication object (an acknowledged publish receipt).

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the publish fails (e.g. not connected).


append_to_table(tablename, *args, **kwargs)

Appends data to a fleet table by calling the registered append procedure at append.<SWARM_KEY>.<APP_KEY>.<tablename>. Unlike publish_to_table, this uses a remote procedure call rather than a pub/sub event.

await ironflock.append_to_table("sensordata", {"temperature": 22.5, "humidity": 60})
Parameter Type Description
tablename str The name of the table, e.g. "sensordata"
*args positional Row data to append
**kwargs keyword Row data as keyword arguments

Returns: Any — The result of the remote procedure call.

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the call fails (e.g. not connected, or the procedure is not registered).


publish_rows_to_table(tablename, rows, **kwargs)

Publishes many rows in a single message (bulk insert) to the dedicated topic bulk.<SWARM_KEY>.<APP_KEY>.<tablename>. The platform inserts the whole batch atomically (all-or-nothing) in one operation. Use this for high-frequency data where one round-trip per row is too costly. Like publish_to_table, this is fire-and-forget — the ack confirms delivery to the router, not the DB insert.

await ironflock.publish_rows_to_table("sensordata", [
    {"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5},
    {"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7},
])
Parameter Type Description
tablename str The name of the table, e.g. "sensordata"
rows List[dict] Non-empty list of row dicts to insert
**kwargs keyword Extra arguments shared by the whole batch

Returns: Publication — The publication object (an acknowledged publish receipt).

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the publish fails (e.g. not connected).


append_rows_to_table(tablename, rows, **kwargs)

Appends many rows in a single RPC (bulk insert) by calling the dedicated procedure appendBulk.<SWARM_KEY>.<APP_KEY>.<tablename>. The platform inserts the whole batch atomically (all-or-nothing): if any row is invalid the entire batch is rejected and nothing is persisted. Prefer this over publish_rows_to_table when you need the insert outcome.

result = await ironflock.append_rows_to_table("sensordata", [
    {"tsp": "2024-01-15T10:30:00.000Z", "temperature": 22.5},
    {"tsp": "2024-01-15T10:30:01.000Z", "temperature": 22.7},
])
# result -> {"success": True, "count": 2}
Parameter Type Description
tablename str The name of the table, e.g. "sensordata"
rows List[dict] Non-empty list of row dicts to insert
**kwargs keyword Extra arguments shared by the whole batch

Returns: Any — The result of the remote procedure call (e.g. {"success": True, "count": N}).

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the bulk append fails (all-or-nothing — no rows were persisted).


report_error(error, level="error", append=False, tsp=None)

Reports an application error into the fleet's error-logs table. This is a convenience wrapper over publish_to_table / append_to_table: it stamps the row with source="app", a severity level, and a timestamp, then writes it like any normal table row. The error lands in the same per-databackend error-logs table that fleetdb system errors use (tagged source="system"), so it is queryable with getHistory, streamable with subscribe_to_table, usable in board-templates, and delivered in realtime on transformed.error-logs — without firing the platform's system-error toast.

# Fire-and-forget (default): publishes to the error-logs table
await ironflock.report_error("Sensor read timed out", level="warn")

# Pass an Exception to capture its traceback (falls back to the message)
try:
    risky_operation()
except Exception as err:
    await ironflock.report_error(err)

# Use the append RPC when you want to await the insert outcome
await ironflock.report_error("Calibration failed", level="error", append=True)
Parameter Type Description
error str | BaseException The error message, or an exception whose traceback (or message) is recorded
level str, optional Severity: "error", "warn", "info" or "debug". Defaults to "error"
append bool, optional When True, use the append RPC (returns the insert outcome). Defaults to False (fire-and-forget publish)
tsp str, optional ISO-8601 timestamp override. Defaults to the current time

Returns: Publication | Any — The publication object (or, with append=True, the RPC result).

Raises: RuntimeError with a descriptive message if the underlying publish/append fails (e.g. not connected).


subscribe(topic, handler, options=None)

Subscribes to a topic on the IronFlock message router.

def on_message(*args, **kwargs):
    print("Received:", args, kwargs)

subscription = await ironflock.subscribe("com.myapp.mytopic", on_message)
Parameter Type Description
topic str The URI of the topic to subscribe to
handler callable Function called when a message is received
options SubscribeOptions, optional Subscription options

Returns: Subscription — The subscription object.

Raises: RuntimeError with a descriptive message if the subscription fails (e.g. not connected).


subscribe_to_table(tablename, handler, options=None)

Convenience function to subscribe to a fleet table. Automatically constructs the correct topic using the SWARM_KEY and APP_KEY environment variables. Receives rows written via both the single-row and the bulk insert paths — rows from a bulk insert are delivered to your handler one at a time, so handler code stays the same.

def on_table_data(*args, **kwargs):
    print("New row:", args, kwargs)

await ironflock.subscribe_to_table("sensordata", on_table_data)
Parameter Type Description
tablename str The name of the table to subscribe to
handler callable Function called when new data arrives
options SubscribeOptions, optional Subscription options

Returns: Subscription — The subscription object.

Raises: RuntimeError with a descriptive message if the subscription fails (e.g. not connected).


getHistory(tablename, queryParams)

Retrieves historical data from a fleet table.

# Simple query with limit
data = await ironflock.getHistory("sensordata", {"limit": 100})

# Query with time range and filters
data = await ironflock.getHistory("sensordata", {
    "limit": 500,
    "offset": 0,
    "timeRange": {
        "start": "2026-01-01T00:00:00Z",
        "end": "2026-03-01T00:00:00Z"
    },
    "filterAnd": [
        {"column": "temperature", "operator": ">", "value": 20},
        {"column": "humidity", "operator": "<=", "value": 80}
    ]
})

# Current value(s) only: add the `latest` marker. The data backend derives
# the latest row per entity in SQL (entity = the table's maintainLatestFlagFor
# columns from the data-template; without one, the single most recent row).
current = await ironflock.getHistory("sensordata", {
    "limit": 100,
    "filterAnd": [{"latest": True}]
})
Parameter Type Description
tablename str The name of the table to query
queryParams dict or TableQueryParams Query parameters (see below)

queryParams fields:

Field Type Description
limit int Maximum number of rows to return (1–10000, required)
offset int, optional Offset for pagination
timeRange dict, optional {"start": "<ISO datetime>", "end": "<ISO datetime>"}
filterAnd list, optional List of AND filter conditions {"column": str, "operator": str, "value": ...}, and/or the {"latest": True} mode marker (see below)
columns List[str], optional Columns to return (tsp, device_key and authid are always included). Omit for all columns

Supported filter operators: =, !=, >, <, >=, <=, LIKE, ILIKE, IN, NOT IN, IS, IS NOT.

Latest values: a {"latest": True} entry in filterAnd is not a WHERE predicate but a mode switch: the data backend returns only the latest row per entity, derived on the fly in SQL (DISTINCT ON over the entity key declared as maintainLatestFlagFor in the table's data-template; a table without an entity key yields the single most recent row). The former physical latest_flag column no longer exists — a legacy {"column": "latest_flag", "operator": "=", "value": True} filter is still accepted and treated as the marker, but new code should use {"latest": True}. Other predicates combine with the marker as expected: entity-key predicates narrow which entities are returned, all other predicates and timeRange filter the resulting latest rows.

Returns: Any — The query result data (typically a list of row dicts).

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message when the history procedure is not registered (table not declared / data backend not running) or the call fails.


get_series_history(tablename, params)

Retrieves down-sampled time-series data from a fleet table — numeric columns aggregated into time namespaces (e.g. hourly averages). Ideal for charts over long time ranges. Available for tables (not transforms).

series = await ironflock.get_series_history("sensordata", {
    "metrics": ["temperature", "humidity"],
    "method": "AVG",
    "limit": 500,
    "timeRange": ["2026-01-01T00:00:00Z", "2026-03-01T00:00:00Z"],
    "groupBy": ["device_id"]
})
Parameter Type Description
tablename str The name of the table to query
params SeriesQueryParams or dict Series query parameters (see below)

params fields:

Field Type Description
metrics List[str] Numeric columns to down-sample
method str Aggregation per namespace: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" or "LAST"
limit int Maximum number of namespaces (1–10000)
timeRange list [start, end] — ISO strings or epoch-ms numbers; None = open end (required)
groupBy List[str], optional Columns to group the series by
filterAnd list, optional AND filter conditions (WHERE predicates only — the {"latest": True} marker is not supported in series queries; use getHistory for latest values)

Returns: Any — The down-sampled series rows.

Raises: ValueError on invalid parameters (including a latest marker in filterAnd); RuntimeError with a descriptive message when the series procedure is not registered or the call fails.


call(topic, args=None, kwargs=None, options=None)

Calls a remote procedure on the IronFlock message router using a full WAMP topic URI.

result = await ironflock.call("some.full.wamp.topic", args=[42])
Parameter Type Description
topic str The full WAMP URI of the procedure to call
args list, optional Positional arguments
kwargs dict, optional Keyword arguments
options CallOptions, optional Call options

Returns: Any — The result of the remote procedure call.

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the call fails (e.g. not connected, or the procedure is not registered).


call_device_function(device_key, topic, args=None, kwargs=None, options=None)

Calls a remote procedure registered by another IronFlock device. Automatically assembles the full WAMP topic as {swarm_key}.{device_key}.{app_key}.{env}.{topic}.

result = await ironflock.call_device_function(42, "com.myapp.myprocedure", args=[42])
Parameter Type Description
device_key int The device key of the target device
topic str The URI of the procedure to call
args list, optional Positional arguments
kwargs dict, optional Keyword arguments
options CallOptions, optional Call options

Returns: Any — The result of the remote procedure call.

Raises: ValueError on invalid parameters; RuntimeError with a descriptive message if the call fails (e.g. not connected, or the procedure is not registered).

call_function() is a deprecated alias for call_device_function().


register_device_function(topic, endpoint, options=None)

Registers a procedure that can be called by other devices in the fleet. Automatically constructs the full WAMP topic as {swarm_key}.{device_key}.{app_key}.{env}.{topic}.

def add(a, b):
    return a + b

await ironflock.register_device_function("com.myapp.add", add)
Parameter Type Description
topic str The URI of the procedure to register
endpoint callable The function to register
options RegisterOptions, optional Registration options

Returns: Registration — The registration object.

Raises: RuntimeError with a descriptive message if the registration fails (e.g. not connected).

register() is an alias for register_device_function(). register_function() is a deprecated alias.


set_device_location(long, lat)

Updates the device's location in the platform master data. The maps in device or group overviews will reflect the new location in realtime.

await ironflock.set_device_location(long=8.6821, lat=50.1109)
Parameter Type Description
long float Longitude (-180 to 180)
lat float Latitude (-90 to 90)

Returns: Any — The result of the location update call.

Raises: ValueError on invalid coordinates; RuntimeError with a descriptive message if the location service call fails.

Note: Location history is not stored. If you need location history, create a dedicated table and use publish_to_table.


getRemoteAccessUrlForPort(port)

Returns the remote access URL for a given port on the device.

url = ironflock.getRemoteAccessUrlForPort(8080)
# e.g. "https://<device_key>-<app_name>-8080.app.ironflock.com"
Parameter Type Description
port int The port number

Returns: str | None — The remote access URL string (e.g. "https://<device_key>-<app_name>-8080.app.ironflock.com"), or None if the device key or app name is not available.


Properties

Property Type Description
is_connected bool True if the connection to the platform is established
connection CrossbarConnection The underlying connection instance (for advanced use)

Lifecycle Methods

Method Description
run() Starts the connection and runs the main function (blocking, synchronous)
await start() Starts the connection asynchronously
await stop() Stops the connection and cancels the main task
await run_async() Starts the connection and keeps it running asynchronously

Cross-App Data Access

Read another app's fleet data from within your app, in the same project and fleet. The provider app must list your app in its data-template consumes: section, and the project user must grant access. Access is read-only: you can query history and subscribe to realtime rows of the tables and transforms (views) the provider shares — you cannot write to them.

from ironflock import IronFlock, CrossAppAccessError

ironflock = IronFlock()
await ironflock.start()

# Open a read-only handle on another app's data backend
weather = await ironflock.connect_to_app("weather-app")

# Inspect what the provider shares (non-private tables / transforms)
print([t["tablename"] for t in weather.tables])

# Query history, just like your own tables
rows = await weather.get_history("forecasts", {"limit": 100})

# Subscribe to realtime rows
def on_forecast(*args, **kwargs):
    print("New forecast:", args)

await weather.subscribe_to_table("forecasts", on_forecast)

# Access errors carry a machine-readable code
try:
    await ironflock.connect_to_app("unshared-app")
except CrossAppAccessError as err:
    print(err.code)  # e.g. "NO_GRANT"

Consumed-app connections are cached per app + stage and are closed automatically by ironflock.stop().

connect_to_app(app_name, stage=None, on_error=None)

Opens a read-only connection to another app's data backend in the same project and returns a ConsumedApp handle. Resolves the provider and connects to its realm using this device's credentials.

weather = await ironflock.connect_to_app("weather-app", stage="prod")
Parameter Type Description
app_name str Provider app name, as declared in your consumes: section
stage str, optional Provider stage to connect to ("dev" or "prod"). Defaults to this app's own stage (ENV)
on_error callable, optional Called with a CrossAppAccessError when the connection is fatally denied after connect_to_app resolved (e.g. the grant is later revoked)

Returns: ConsumedApp — A read-only handle on the provider's data backend.

Raises: CrossAppAccessError (code: NO_GRANT, PROVIDER_NOT_INSTALLED, UNKNOWN_APP, or NOT_AUTHORIZED).


ConsumedApp handle

Returned by connect_to_app. A read-only view of a provider app's shared tables and transforms.

Properties:

Property Type Description
app str Provider app name
stage str Provider stage this handle is connected to ("dev" or "prod")
tables List[dict] Non-private tables the provider shares (dicts with tablename, optional description/columns)
transforms List[dict] Non-private transforms (views) the provider shares (same shape as tables)
is_connected bool True while the connection to the provider is open
connection CrossbarConnection The underlying connection (advanced use)

consumed_app.get_history(tablename, query_params=None)

Queries history rows of a shared table or transform. Takes the same query parameters as getHistory (limit, offset, timeRange, filterAnd, columns) — including the {"latest": True} marker in filterAnd for reading the provider's current values.

rows = await weather.get_history("forecasts", {"limit": 100})
current = await weather.get_history("forecasts", {
    "limit": 100,
    "filterAnd": [{"latest": True}],
})

Returns: Any — The query result rows.

consumed_app.subscribe_to_table(tablename, handler, options=None)

Subscribes to realtime rows of a shared table or transform. Bulk-inserted rows are delivered one at a time, exactly like subscribe_to_table on your own app.

def on_forecast(*args, **kwargs):
    print("New forecast:", args)

await weather.subscribe_to_table("forecasts", on_forecast)

Returns: Any — The subscription object.

consumed_app.get_series_history(tablename, params)

Queries down-sampled time-series history of a shared table (not available for transforms).

series = await weather.get_series_history("forecasts", {
    "metrics": ["temperature"],
    "method": "AVG",
    "limit": 500,
    "timeRange": ["2026-01-01T00:00:00Z", "2026-03-01T00:00:00Z"],
})

params fields (SeriesQueryParams):

Field Type Description
metrics List[str] Numeric columns to down-sample
method str Aggregation per namespace: "AVG", "SUM", "COUNT", "MIN", "MAX", "FIRST" or "LAST"
limit int Maximum number of rows (1–10000)
timeRange list [start, end] — ISO strings or epoch-ms numbers; None = open end (required)
groupBy List[str], optional Columns to group the series by
filterAnd list, optional AND filter conditions (WHERE predicates only — no {"latest": True} marker)

Returns: Any — The down-sampled series rows.

consumed_app.close()

Closes this handle's connection to the provider. (All consumed-app connections are also closed by ironflock.stop().)

Returns: None


CrossAppAccessError

Raised by connect_to_app and the ConsumedApp methods when cross-app access is denied or misused. Exposes a machine-readable code:

code Meaning
NO_GRANT The project user has not granted your app access to the provider
PROVIDER_NOT_INSTALLED The provider app has no data backend for that stage in this project
UNKNOWN_APP No app by that name
PRIVATE_TABLE The requested table/transform is not in the provider's shared catalog
NOT_AUTHORIZED The router/provider denied access (e.g. the grant was revoked)

Managed File Storage

Every app databackend gets private object storage alongside its tables. With no files: section in the data template you still get one namespace named default, so this works against apps released before FleetFiles existed.

# Store an object and get a permanent URL back in the same call
info = await ironflock.files.put("part-1.jpg", jpeg_bytes, content_type="image/jpeg")

# That URL is safe to put in a FleetDB column — a dashboard widget can render
# <img src="{{photo_url}}"> and it just works
await ironflock.publish_to_table("inspections", part_id="1", photo_url=info.url)

data = await ironflock.files.get("part-1.jpg")
async for obj in ironflock.files.iter(prefix="2026/"):
    print(obj.key, obj.size)

The URL never expires, yet stays readable only to an authenticated requestor holding READ on this databackend — an auth proxy re-checks on every request, so it is safe to store but not a public link.

A namespace is a key prefix that carries policy — retention, sharing, allowed content types. It is not a separate S3 bucket; every namespace lives in the app's one bucket. Declare one only when a set of objects needs different rules; otherwise stay in default and organise with key paths.

Declare additional namespaces in data-template.yml:

files:
  namespaces:
    - name: frames
      description: Raw camera frames, one JPEG per inspected part.
      contentTypes: ["image/jpeg"]
      maxObjectBytes: 20971520
      retention: { deleteAfter: 30 days }

files.put(key, data, namespace="default", content_type=None)

Stores bytes. Returns ObjectInfo with key, size, etag, content_type and url.

files.get(key, namespace="default")

Returns bytes.

files.list(prefix="", namespace="default", limit=200, cursor="")

One page: .objects, .prefixes, .is_truncated, .cursor.

files.iter(prefix="", namespace="default")

Async iterator over every object under a prefix, paginating for you.

files.stat(key) / files.exists(key)

Metadata without transferring; exists returns a bool.

files.delete(key) / files.copy(key, to) / files.move(key, to)

move is copy-then-delete on the client (the service has no move verb), so it is not atomic — a failed delete leaves both copies.

files.put_file(key, path) / files.get_to_file(key, path)

Convenience wrappers over a local file.

files.url(key, namespace="default", version=None)

The permanent URL. Returns None where the deployment has no HTTP edge (a plain-HTTP appliance) — the signal to fall back to files.get(). Pass an ETag as version to let browsers cache it immutably.

files.namespaces() / files.usage() / files.catalog()

What this app may use and how much it has stored. catalog() also reports inline_max_bytes and public_base_url; it is cached after the first call.

files.share_url(key, namespace="default", ttl=900)

An expiring link anyone holding it can fetch. Unlike files.url() this is a bearer capability — nothing re-checks authorization when it is used. Hand it to a person; do not store it in a column. The server clamps ttl.

files.upload_url(key, namespace="default", ttl=3600, content_type=None, size=None)

An expiring URL that accepts a direct upload. Returns url, method, headers and expires_in; send exactly those headers or the signature will not verify.

Large objects

The SDK picks the transport by size, automatically:

Size Path
inline_max_bytes (6 MiB) one WAMP call
larger direct to object storage over HTTPS, bypassing the router

put_file() and get_to_file() stream from and to disk on the direct path, so a multi-gigabyte object never has to fit in memory. put() and get() work on bytes and therefore do hold the object in memory — prefer the file variants above a few hundred MB.

Two ceilings remain, and both raise TOO_LARGE with a reason that says which one you hit:

  • 5 GiB — S3's single-upload limit. Multipart is not implemented yet.
  • inline_max_bytes where there is no direct endpoint — an air-gapped appliance cannot transfer a large object at all. The message says so, because no retry or smaller chunk can help.

The direct path needs the device to reach the object store host, not just the router. Two field failures have their own codes rather than looking like auth problems: PRESIGN_UNREACHABLE (a proxy allowing only the router) and CLOCK_SKEW (S3 rejects requests more than 15 minutes out of step — check NTP).

FileStoreError

Every failure raises FileStoreError with a stable .code and a human-readable .reason. Branch on .code, never on .reason.

from ironflock.filestore import FileStoreError

try:
    await ironflock.files.put("huge.bin", payload)
except FileStoreError as e:
    if e.code == "QUOTA_EXCEEDED":
        ...
Code Meaning
NOT_AUTHORIZED Caller may not perform this operation
NO_SUCH_NAMESPACE Namespace is not declared in the data template
NO_SUCH_OBJECT Key does not exist
TOO_LARGE Exceeds the single-call transfer limit
OBJECT_TOO_LARGE Exceeds the namespace's own maxObjectBytes
QUOTA_EXCEEDED Filestore is full
CONTENT_TYPE_NOT_ALLOWED Namespace restricts contentTypes
NOT_SUPPORTED Backend cannot do this
NOT_AVAILABLE No file service on this deployment
PRESIGN_UNREACHABLE Object store not reachable directly (proxy?)
CLOCK_SKEW Device clock too far out of step for S3
INTERNAL Anything else

A newer server may add codes; unknown ones pass through as .code rather than crashing, so treat anything unrecognised as a generic failure.

Advanced Usage

If you need more control, e.g. acting on lifecycle events (onJoin, onLeave) take a look at the examples folder.

Development

This project uses uv for dependency management and building.

Install uv if you don't have it:

curl -LsSf https://astral.sh/uv/install.sh | sh

Install dependencies (including dev dependencies):

uv sync --extra dev

Run tests:

just test-unit    # Run unit tests only
just test         # Run all tests
just test-docker  # Run integration tests with Docker

Build and publish a new pypi package:

just publish

Or manually:

# Clean previous builds
rm -rf dist

# Build the package
uv build

# Upload to PyPI
uv publish

Check the package at https://pypi.org/project/ironflock/.

Test Deployment

To test the package before deploying to PyPI you can use test.pypi.

just publish-test

Or manually:

uv build
uv publish --publish-url https://test.pypi.org/legacy/

Once the package is published you can install it from TestPyPI:

pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ironflock

Once the package is published you can use it in other code by putting these lines at the top of the requirements.txt

--index-url https://test.pypi.org/simple/
--extra-index-url https://pypi.org/simple/

Download files

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

Source Distribution

ironflock-1.7.0.tar.gz (58.6 kB view details)

Uploaded Source

Built Distribution

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

ironflock-1.7.0-py3-none-any.whl (53.8 kB view details)

Uploaded Python 3

File details

Details for the file ironflock-1.7.0.tar.gz.

File metadata

  • Download URL: ironflock-1.7.0.tar.gz
  • Upload date:
  • Size: 58.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.20 {"installer":{"name":"uv","version":"0.9.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ironflock-1.7.0.tar.gz
Algorithm Hash digest
SHA256 d1ce50539566ac0b26f101a3894fec78c8ab7a485dd66db3116534aa29c0a03e
MD5 cdc1daf82e0de31f3ca475542fb64101
BLAKE2b-256 f91270b0144713033c6c38f29563502d4cd6f574f5726d5f38d0313f5709e70f

See more details on using hashes here.

File details

Details for the file ironflock-1.7.0-py3-none-any.whl.

File metadata

  • Download URL: ironflock-1.7.0-py3-none-any.whl
  • Upload date:
  • Size: 53.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.20 {"installer":{"name":"uv","version":"0.9.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ironflock-1.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7ca17c033320e6ef0e3374cada35aa38c2a0b61659229ad02e102e625284a029
MD5 9642c2c22d3f745532d078f3b8cd6c1c
BLAKE2b-256 77316a560a4193e1bbb49ea50f1d77bd13effee9a65320326386354d31207741

See more details on using hashes here.

Release history Release notifications | RSS feed

1.7.2

2 files

This release

1.7.0 This release

2 files

1.6.3

2 files

1.6.2

2 files

1.6.1

2 files

1.6.0

2 files

1.5.4

2 files

1.5.3

2 files

1.5.2

2 files

1.5.1

2 files

1.5.0

2 files

1.4.3

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.18

2 files

1.3.17

2 files

1.3.16

2 files

1.3.15

2 files

1.3.14

2 files

1.3.13

2 files

1.3.12

2 files

1.3.11

2 files

1.3.10

2 files

1.3.9

2 files

1.3.8

2 files

1.3.7

2 files

1.3.6

2 files

1.3.5

2 files

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.3.0

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

2 files

1.0.7

2 files

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

1.0.0

2 files

0.1.1

2 files

0.1.0

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