A hierarchical, state-inheriting command and request builder.
Project description
Slug Farm
Status: “Works for me” beta, pre-alpha ideation for basically everyone else.
For my personal centralized task scheduling, this is effectively beta (it does what I need).
For a broader audience / general-purpose library use, it’s early alpha at best, and honestly closer to pre-alpha ideation.
“Slug”
Package gets its name because the original idea was to boil any granule of functionality to string(the slug, in a strict sense) a command, and kwargs.
- The object is a
Slug(orBashSlug,RequestSlug, etc.). - The slug (conceptually) is the name used to address that object inside a registry.
So the “slug” is ultimately the registry key, which is a slug in the strict sense for the functionality
What this is
Slug Farm is a framework for reducing disparate kinds of work into one callable shape:
result = some_slug(command="...", task_kwargs={...})
There is also a SlugRegistry which holds these slugs further abstrating the slug to an identifiable string
result is intended to always be a SlugResult
Core Use Case
I built this for centralized task scheduling, where I want to call tasks across REST APIs and shell environments etc. This allows me to keep tasks in a database in an easily patchable form. I can also define large collections of tasks quickly by “walking” through and capturing all viable intermediates as functions, which is especially helpful with bash-like command structures or API endpoint trees.
The real value is persistence + uniformity:
Any task becomes: (slug name (str), command (str), kwargs (dict))
Discovered Benefit
Once you get the hang of it, declarating a tree of functions is quite streamlined and readable. I've been able to write python functions for a tree RestAPI endpoints in a way that is clear to understand and maps quite cleanly to the APIs Swagger documentation.
The Challenge
To support this minimal branching declaration
If you’re thinking:
- “I need a way to call a bunch of operations”
- “I want structured access to a set of endpoints”
- “I want composable function calls”
…then again: a dictionary of functions is probably better.
It’s simpler, clearer, easier to type-check, easier to debug, and WAYY less opinionated.
The “juice” you get from Slug Farm is real, but the “squeeze” is learning a structure that is necessarily rigid and ultimitely quite arbitrary.
What’s in here right now
BashSlug
Builds a command + flags list and executes with subprocess.
- Intentionally does not interpret pipes or shell operators.
- Literal
|is treated as an argument, not an operator (this is a security decision).
RequestSlug
Builds an HTTP request using requests, with inherited URL segments, params merging, and JSON-body logic.
- Supports placeholders like
/crops/{crop_name}. Which can be "intelligently" (hopefully) replaced with kwargs during call - GET carries params; POST/PUT/PATCH carry params and JSON bodies (with filtering support).
UDP_Slug
Sends UDP payloads (optionally in bursts) with a shared UUID per run.
PythonSlug
Wraps a Python callable so it fits the same (command, task_kwargs) invocation style. This exist entirely if you're benefitting heavily from the SlugRegistry for Bash and Request and would like use the same structure for Python slugs. This would be quite silly to use in a vacuum.
Registry (the part that makes the naming make sense)
The registry concept is the “why” behind the name Slug Farm:
- the Slug object is the implementation
- the slug name is the address (registry key)
- the registry is what makes tasks easy to store/replay/patch
Right now:
- A SlugRegistry exists, but it’s not fully built out into the persistence-oriented system I want.
- I still need to expand this area and add tests around it.
- YAML declarative registry would be cool. Ways to inject custom slug classes into declarative yaml are being currently mulled over.
Installation
pip install slug_farm
From source:
git clone <repo>
cd SlugFarm
pip install -e ".[dev]"
Quickstart
BashSlug (dry run)
from slug_farm import BashSlug
git = BashSlug(name = "git", command = "git")
status = git.branch(branch_name = "status", command="status")
result = status(task_kwargs = {'untracked-status': 'no', 's': True}) # executes "git status -s --untracked-status no"
RequestSlug (dry run)
from slug_farm import RequestSlug
api = RequestSlug(
name = "api",
base_url="https://example.com/v1",
headers = SOME_HEADERS_DEFINED_ELSEWHERE
)
crops = api.branch( # Inherits headers, params, etc. Default method is GET
branch_name = "crops",
url_segment="crops"
)
get_result = crops(task_kwargs={"limit": 10})
add_crops = crops.branch(
branch_name= "add",
url_segment="add",
method="POST"
)
add_crops(task_kwargs = {"apples": 10}) # Sends a post request to https://example.com/v1/crops/add, presumably to add 10 apples.
# Here is where shoehorning RestAPI requests into (command, kwargs)-only structure adds some clunk.
# exclude_params makes sure the crop_id param isn't passed and is only swapped in the url placeholder
# If the endpoint is okay getting it as a param too you don't need to bother
get_specific = crops.branch(
branch_name = "specific",
url_segment="{crop_id}",
exclude_params="crop_id"
)
get_specific(task_kwargs = {"crop_id": "crop_2b67d"}) # Sends a GET request to https://example.com/v1/crops/crop_2b67d, utilizing the placeholder
UDP_Slug
from slug_farm import UDP_Slug
slug = UDP_Slug(
"telemetry",
url="127.0.0.1",
port=9999,
burst_size=5, # Will send a burst of 5 identical messages. All messages within burst will contain the same uuid4 so the listener can deduplicate.
burst_delay_ms=50,
)
result = slug(task_kwargs={"hello": "world"}) # Command arg is generally excluded. If you include it to UDP it adds a "message" key and puts your command there. Should probably just be "command" though. Looking into it eventually
print(result.ok, result.status)
Contributing / expectations
If you’re looking at this repo with a “could this be useful?” mindset:
PRs and tests are welcome, especially around:
- registry behavior
- persistence patterns
- clearer docs/examples
- Intelligent and clear error messages.
- With a structure this opinionated, error messages should be able to be incredibly readable and informative without making it more opinionated to support that. This does not exist yet.
- logging integration
- edge-case safety
- Declarative initiation and hydration
- Again, one of the benefits of being opinionated is that a 'suite' of slugs might be able to be declared with a simple yaml file, or automatically collected from a script. This could even potentially be procedurally generated from a Swagger page.
- Hydrating a slug registry from a YAML also offers the opportunity to pre-inspect a config and surface some more errors/hints of such an opinionated library in a way which is difficult to do with docstrings/linting alone.
License
MIT. See LICENSE.
Project details
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 slug_farm-0.1.2.tar.gz.
File metadata
- Download URL: slug_farm-0.1.2.tar.gz
- Upload date:
- Size: 85.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f035796fb5800315544fe9961944f944849c4c0e353c979acab284c3e4f5d7a
|
|
| MD5 |
ce2f0e2598cb55fc4978e7b2662305fd
|
|
| BLAKE2b-256 |
d3289571b6692226622c02425f91578400cb4dc2a4cbe25fdce7dfba2bb5327c
|
File details
Details for the file slug_farm-0.1.2-py3-none-any.whl.
File metadata
- Download URL: slug_farm-0.1.2-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
211025c747c535ad23da0ea690d628efa26bb9bfdbbcccc84ce16dba508fbdb5
|
|
| MD5 |
ab3e1e67d96f2351ab5c542430bc84f6
|
|
| BLAKE2b-256 |
d937aca615fe57cb540f3464d64d9f3de78976776de9ef04d91e8fc7d740de7a
|