uf#
uf - UI Fast: Minimal-boilerplate web UIs for Python functions.
uf bridges functions to HTTP services (via qh) to Web UI forms (via ju.rjsf), following the “convention over configuration” philosophy.
Basic usage:
>>> from uf import mk_rjsf_app
>>>
>>> def add(x: int, y: int) -> int:
... '''Add two numbers'''
... return x + y
>>>
>>> app = mk_rjsf_app([add])
>>> # app.run() # Start the web server
The main entry points are:
mk_rjsf_app: Create a web app from functions (functional interface)UfApp: Object-oriented wrapper with additional conveniencesFunctionSpecStore: Manage function specifications (advanced usage)
Advanced features:
ui_config: Decorator for UI metadataRjsfFieldConfig: Field configuration classFunctionGroup: Group functions for organizationInputTransformRegistry: Custom type transformationsField dependencies and interactions
Testing utilities
Functions
|
Create an RJSF-backed web app from a list of functions. |
|
Get a predefined field configuration by name. |
|
Apply field configurations to existing schemas. |
|
Register a type in the global registry. |
Get the global transformation registry. |
|
|
Create a uf app with grouped function navigation. |
|
Automatically group functions by name prefix. |
|
Automatically group functions by their module. |
|
Automatically group functions by a tag attribute. |
|
Decorator to add UI configuration to functions. |
|
Decorator to assign a function to a group. |
|
Decorator to hide a function from the UI. |
|
Decorator to configure specific fields of a function. |
|
Decorator to attach example arguments to a function. |
|
Decorator to mark a function as deprecated. |
|
Decorator to mark a function as requiring authentication. |
|
Decorator to mark a function with rate limiting metadata. |
|
Get UI configuration from a function. |
|
Get group name from a function. |
|
Get field configurations from a function. |
|
Check if a function is hidden from UI. |
|
Get example arguments from a function. |
|
Decorator to attach field dependencies to a function. |
|
Get field dependencies from a function. |
|
Test a function with form-like input. |
|
Assert that a specification is valid RJSF format. |
|
Assert that a spec has a specific field. |
|
Assert that a field has the expected type. |
|
Assert that a field is required. |
Get the global renderer registry. |
|
|
Register a renderer in the global registry. |
|
Render a result using the global registry. |
|
Decorator to specify result renderer for a function. |
|
Get the result renderer type for a function. |
|
Check if a function is async. |
|
Convert an async function to a synchronous wrapper. |
|
Convert any async functions in the list to sync wrappers. |
|
Decorator to add timeout to async functions. |
|
Decorator to add retry logic to async functions. |
|
Check if an object is a Pydantic model. |
|
Convert a Pydantic model to JSON Schema. |
|
Check if a function uses Pydantic models in its signature. |
|
Wrap a function that uses Pydantic models. |
|
Convert Pydantic model instance to dictionary. |
|
Convert dictionary to Pydantic model instance. |
Get the global history manager. |
|
|
Decorator to enable call history for a function. |
|
Decorator to require authentication for a function. |
Get the global authentication backend. |
|
|
Decorator to cache function results. |
Get the global cache backend. |
|
|
Decorator to run function in background. |
|
Get a global task queue by name. |
|
Generate OpenAPI 3.0 specification. |
|
Add OpenAPI routes to an app. |
|
Decorator to add webhooks to a function. |
Get the global webhook manager. |
|
|
Get a built-in theme by name. |
Classes
|
Wrapper class for uf applications. |
|
A mapping from function names to their RJSF specifications. |
|
Configuration for individual form fields. |
Builder for RJSF configurations with sensible defaults. |
|
|
Configuration for conditional field display. |
Registry for custom type transformations. |
|
|
Group of functions with metadata. |
Organize functions into groups and hierarchies. |
|
|
Define a dependency between form fields. |
|
Actions that can be triggered by field dependencies. |
Builder for creating field dependencies. |
|
|
Test client for uf applications. |
|
Context manager for testing uf apps. |
Builder for constructing form data for tests. |
|
Base class for result renderers. |
|
Registry for result renderers. |
|
|
Wrapper for async functions that provides both sync and async access. |
|
Record of a single function call. |
|
Manage history of function calls. |
|
A saved parameter preset for a function. |
Manage parameter presets for functions. |
|
|
Combined manager for history and presets. |
|
User account information. |
Base authentication backend. |
|
Simple in-memory dictionary-based authentication. |
|
|
Manage user sessions. |
|
API key for programmatic access. |
|
Manage API keys for programmatic access. |
Base class for cache backends. |
|
|
In-memory cache backend. |
|
Disk-based cache backend using pickle. |
|
Represents a background task. |
|
Status of a background task. |
|
FIFO queue for background tasks. |
|
Configuration for OpenAPI generation. |
|
Represents a webhook event. |
|
Client for sending webhooks. |
Manage webhooks for multiple URLs and events. |
|
|
UI theme configuration. |
|
Configuration for theme system. |
- class uf.ApiKey(key, name, permissions=None, expires_at=None)[source]#
Bases:
objectAPI key for programmatic access.
- key#
The API key string
- name#
Descriptive name
- permissions#
List of allowed permissions
- created_at#
When key was created
- expires_at#
Optional expiration
- is_active#
Whether key is active
- class uf.ApiKeyManager(key_prefix='sk_')[source]#
Bases:
objectManage API keys for programmatic access.
Example
>>> api_keys = ApiKeyManager() >>> key = api_keys.create_key('mobile_app', permissions=['read']) >>> print(f"Your API key: {key.key}") >>> # Later, validate >>> if api_keys.validate_key(key.key, 'read'): ... # Allow access
- class uf.AsyncFunctionWrapper(func)[source]#
Bases:
objectWrapper for async functions that provides both sync and async access.
This allows the same function to be called either way, depending on the context.
Example
>>> async def fetch_user(user_id: int): ... # async database query ... return user_data >>> >>> wrapper = AsyncFunctionWrapper(fetch_user) >>> # Sync call >>> user = wrapper.call_sync(user_id=123) >>> # Async call >>> user = await wrapper.call_async(user_id=123)
- class uf.AuthBackend[source]#
Bases:
objectBase authentication backend.
Subclass this to create custom authentication backends.
- create_user(username, password, roles=None, permissions=None, **metadata)[source]#
Create a new user.
- class uf.CacheBackend[source]#
Bases:
objectBase class for cache backends.
- class uf.CallHistory(max_size=100)[source]#
Bases:
objectManage history of function calls.
Example
>>> history = CallHistory(max_size=100) >>> history.record('add', {'x': 10, 'y': 20}, result=30) >>> recent = history.get_recent('add', limit=5)
- classmethod from_dict(data, max_size=100)[source]#
Create from dictionary.
- Parameters:
- Return type:
- Returns:
CallHistory instance
- get_recent(func_name, limit=10)[source]#
Get recent calls for a function.
- Parameters:
- Return type:
- Returns:
List of recent FunctionCall objects (newest first)
- get_successful_calls(func_name, limit=10)[source]#
Get recent successful calls.
- Parameters:
- Return type:
- Returns:
List of successful FunctionCall objects
- class uf.ConditionalFieldConfig(field_name, *, condition, then_schema=None, else_schema=None)[source]#
Bases:
objectConfiguration for conditional field display.
Allows fields to be shown/hidden based on the values of other fields.
Example
>>> # Show 'other_reason' field only when reason is 'other' >>> config = ConditionalFieldConfig( ... 'other_reason', ... condition={'reason': {'const': 'other'}} ... )
- class uf.DependencyAction(*values)[source]#
Bases:
EnumActions that can be triggered by field dependencies.
- class uf.DependencyBuilder[source]#
Bases:
objectBuilder for creating field dependencies.
Provides a fluent interface for defining dependencies between fields.
Example
>>> builder = DependencyBuilder() >>> builder.when('reason').equals('other').show('other_reason') >>> builder.when('age').greater_than(18).enable('alcohol_consent') >>> dependencies = builder.build()
- build()[source]#
Build and return the list of dependencies.
- Return type:
- Returns:
List of FieldDependency objects
- custom(condition)[source]#
Condition: custom callable.
- disable(target_field)[source]#
Action: disable target field.
- Parameters:
target_field (
str) – Name of field to disable- Return type:
- Returns:
Self for method chaining
- enable(target_field)[source]#
Action: enable target field.
- Parameters:
target_field (
str) – Name of field to enable- Return type:
- Returns:
Self for method chaining
- equals(value)[source]#
Condition: field equals value.
- Parameters:
value (
Any) – Value to compare against- Return type:
- Returns:
Self for method chaining
- greater_than(value)[source]#
Condition: field is greater than value.
- Parameters:
value (
Any) – Value to compare against- Return type:
- Returns:
Self for method chaining
- hide(target_field)[source]#
Action: hide target field.
- Parameters:
target_field (
str) – Name of field to hide- Return type:
- Returns:
Self for method chaining
- in_list(values)[source]#
Condition: field value is in list.
- Parameters:
values (
list) – List of acceptable values- Return type:
- Returns:
Self for method chaining
- less_than(value)[source]#
Condition: field is less than value.
- Parameters:
value (
Any) – Value to compare against- Return type:
- Returns:
Self for method chaining
- make_optional(target_field)[source]#
Action: make target field optional.
- Parameters:
target_field (
str) – Name of field to make optional- Return type:
- Returns:
Self for method chaining
- not_equals(value)[source]#
Condition: field does not equal value.
- Parameters:
value (
Any) – Value to compare against- Return type:
- Returns:
Self for method chaining
- require(target_field)[source]#
Action: make target field required.
- Parameters:
target_field (
str) – Name of field to require- Return type:
- Returns:
Self for method chaining
- show(target_field)[source]#
Action: show target field.
- Parameters:
target_field (
str) – Name of field to show- Return type:
- Returns:
Self for method chaining
- class uf.DictAuthBackend[source]#
Bases:
AuthBackendSimple in-memory dictionary-based authentication.
Suitable for development and simple applications.
Example
>>> backend = DictAuthBackend() >>> backend.create_user('admin', 'secret', roles=['admin']) >>> user = backend.authenticate('admin', 'secret')
- create_user(username, password, roles=None, permissions=None, **metadata)[source]#
Create a new user.
- Return type:
- classmethod from_dict(users_data)[source]#
Create backend from dictionary.
- Parameters:
users_data (
dict) – Dictionary mapping usernames to user info- Return type:
Example
>>> backend = DictAuthBackend.from_dict({ ... 'admin': {'password': 'secret', 'roles': ['admin']}, ... 'user': {'password': 'pass', 'roles': ['user']}, ... })
- class uf.DiskCache(cache_dir='.uf_cache', default_ttl=3600)[source]#
Bases:
CacheBackendDisk-based cache backend using pickle.
Persists cache to disk, suitable for larger datasets or persistence across restarts.
Example
>>> cache = DiskCache(cache_dir='/tmp/uf_cache') >>> cache.set('expensive_result', big_data)
- class uf.FieldDependency(source_field, target_field, condition, action, else_action=None)[source]#
Bases:
objectDefine a dependency between form fields.
- source_field#
Name of the field that triggers the dependency
- target_field#
Name of the field affected by the dependency
- condition#
Function that takes source value and returns bool
- action#
Action to perform when condition is true
- else_action#
Optional action when condition is false
Example
>>> # Show 'other_reason' only when reason is 'other' >>> dep = FieldDependency( ... source_field='reason', ... target_field='other_reason', ... condition=lambda v: v == 'other', ... action=DependencyAction.SHOW, ... else_action=DependencyAction.HIDE ... )
- check(value)[source]#
Check the condition and return the appropriate action.
- Parameters:
value (
Any) – Value of the source field- Return type:
- Returns:
The action to perform
- class uf.FormDataBuilder[source]#
Bases:
objectBuilder for constructing form data for tests.
Provides a fluent interface for building test form data.
Example
>>> form_data = ( ... FormDataBuilder() ... .field('name', 'John Doe') ... .field('email', 'john@example.com') ... .field('age', 30) ... .build() ... )
- field(name, value)[source]#
Add a field to the form data.
- Parameters:
- Return type:
- Returns:
Self for method chaining
- class uf.FunctionCall(func_name, params, result=None, success=True, error=None)[source]#
Bases:
objectRecord of a single function call.
- func_name#
Name of the function
- params#
Parameters used
- result#
Result returned (if captured)
- timestamp#
When the call was made
- success#
Whether the call succeeded
- error#
Error message if failed
- class uf.FunctionGroup(name, funcs=<factory>, description='', icon=None, order=0, collapsed=False)[source]#
Bases:
objectGroup of functions with metadata.
- name#
Name of the group
- funcs#
Functions in this group: callables (partials and callable instances are fine; they are named as
uf.specs.name_of_funcdoes), or a{name: callable}mapping to name them explicitly
- description#
Description of the group
- icon#
Optional icon identifier for the group
- order#
Display order (lower numbers first)
- collapsed#
Whether the group starts collapsed in UI
Example
>>> math_funcs = FunctionGroup( ... 'Math', ... [add, subtract, multiply, divide], ... description='Mathematical operations', ... icon='calculator' ... )
- add_function(func, *, name=None)[source]#
Add a function to this group.
- Parameters:
- Return type:
- Returns:
Self for method chaining
- Raises:
ValueError – If a different callable already has that name in the group
- class uf.FunctionOrganizer[source]#
Bases:
objectOrganize functions into groups and hierarchies.
This class provides a fluent interface for building function organization structures that can be used to generate grouped navigation in the UI.
Example
>>> organizer = FunctionOrganizer() >>> organizer.group('Admin', [user_create, user_delete], icon='shield') >>> organizer.group('Reports', [generate_report, export_csv], icon='file') >>> groups = organizer.get_groups()
- add_to_group(group_name, func)[source]#
Add a function to an existing group.
- Parameters:
- Return type:
- Returns:
Self for method chaining
- Raises:
ValueError – If group doesn’t exist
- add_ungrouped(func)[source]#
Add a function without a group.
- Parameters:
func (
Callable) – Function to add- Return type:
- Returns:
Self for method chaining
- get_groups()[source]#
Get all groups, sorted by order.
- Return type:
- Returns:
List of FunctionGroup objects sorted by order
- group(name, funcs=None, *, description='', icon=None, order=0, collapsed=False)[source]#
Create and add a function group.
- Parameters:
- Return type:
- Returns:
The created FunctionGroup
Example
>>> organizer.group( ... 'Database', ... [save_record, load_record], ... description='Database operations', ... icon='database' ... )
- class uf.FunctionSpecStore(funcs, *, rjsf_config=None, ui_schema_factory=None, param_to_prop_type=None, validate=False)[source]#
Bases:
MappingA mapping from function names to their RJSF specifications.
Lazily generates and caches form specs for each function.
- Parameters:
funcs (
Union[Iterable[Callable],Mapping[str,Callable]]) – Iterable of callables to generate specs for (named byname_of_func:__name__, the wrapped function’s for afunctools.partial, the class name for a callable instance), or a mapping{name: callable}to name them explicitlyrjsf_config (
Optional[dict]) – Optional configuration dict for RJSF generationui_schema_factory (
Optional[Callable]) – Optional callable to customize UI schema generationparam_to_prop_type (
Optional[Callable]) – Optional callable to map parameters to property typesvalidate (
bool) – If true, every function is checked and its spec generated at construction, so a callable uf can’t build a form for fails there, naming it, rather than on the first request (default false: checked on first access of its spec;mk_rjsf_appvalidates).
Example
>>> def add(x: int, y: int) -> int: ... '''Add two numbers''' ... return x + y >>> specs = FunctionSpecStore([add]) >>> 'add' in specs True >>> spec = specs['add'] >>> 'schema' in spec True
- class uf.HistoryManager(max_history=100)[source]#
Bases:
objectCombined manager for history and presets.
Provides a unified interface for tracking calls and managing presets.
Example
>>> manager = HistoryManager() >>> manager.record_call('add', {'x': 10, 'y': 20}, result=30) >>> manager.save_preset('quick', 'add', {'x': 10, 'y': 20}) >>> recent = manager.get_recent_calls('add') >>> presets = manager.get_presets('add')
- get_recent_calls(func_name, limit=10)[source]#
Get recent calls.
- Parameters:
- Return type:
- Returns:
List of recent FunctionCall objects
- classmethod load_from_file(filepath, max_history=100)[source]#
Load from file.
- Parameters:
- Return type:
- Returns:
HistoryManager instance
- record_call(func_name, params, result=None, success=True, error=None)[source]#
Record a function call.
- class uf.InputTransformRegistry[source]#
Bases:
objectRegistry for custom type transformations.
Integrates with qh’s type registry and extends it for UI needs, allowing custom types to be properly handled in both the form interface and the HTTP service layer.
Example
>>> from datetime import datetime >>> registry = InputTransformRegistry() >>> >>> # Register a custom type >>> registry.register_type( ... datetime, ... to_json=lambda dt: dt.isoformat(), ... from_json=lambda s: datetime.fromisoformat(s), ... ui_widget='datetime' ... )
- get_ui_config(py_type)[source]#
Get UI configuration for a type.
- Parameters:
py_type (
Type) – Python type to look up- Return type:
- Returns:
RjsfFieldConfig or None if not registered
- mk_input_trans_for_funcs(funcs)[source]#
Create input transformation compatible with qh.
This creates a transformation function that can be passed to qh.mk_app as the input_trans parameter.
- Parameters:
funcs (
list[Callable]) – List of functions to create transformation for- Return type:
- Returns:
Transformation function for qh
Example
>>> from uf import mk_rjsf_app >>> registry = InputTransformRegistry() >>> # ... register types ... >>> input_trans = registry.mk_input_trans_for_funcs([my_func]) >>> app = mk_rjsf_app([my_func], input_trans=input_trans)
- mk_output_trans()[source]#
Create output transformation for qh.
- Return type:
- Returns:
Transformation function for qh output
Example
>>> output_trans = registry.mk_output_trans() >>> app = mk_rjsf_app([my_func], output_trans=output_trans)
- register_type(py_type, *, to_json=None, from_json=None, ui_widget=None, ui_config=None, json_schema_type=None, json_schema_format=None)[source]#
Register a type with both qh and UI configuration.
- Parameters:
py_type (
Type) – Python type to registerto_json (
Optional[Callable[[Any],Any]]) – Function to convert Python type to JSON-serializablefrom_json (
Optional[Callable[[Any],Any]]) – Function to convert JSON to Python typeui_widget (
Optional[str]) – RJSF widget to use for this typeui_config (
Optional[RjsfFieldConfig]) – Full RjsfFieldConfig for this typejson_schema_type (
Optional[str]) – JSON Schema type (e.g., ‘string’, ‘number’)json_schema_format (
Optional[str]) – JSON Schema format (e.g., ‘date-time’, ‘email’)
- Return type:
Example
>>> from pathlib import Path >>> registry.register_type( ... Path, ... to_json=str, ... from_json=Path, ... ui_widget='text', ... json_schema_type='string' ... )
- class uf.MemoryCache(default_ttl=3600, max_size=1000)[source]#
Bases:
CacheBackendIn-memory cache backend.
Simple dictionary-based caching suitable for single-process applications.
Example
>>> cache = MemoryCache(default_ttl=3600) >>> cache.set('key', 'value', ttl=60) >>> value = cache.get('key')
- class uf.OpenAPIConfig(title='API', version='1.0.0', description='', servers=None, enable_swagger=True, enable_redoc=True)[source]#
Bases:
objectConfiguration for OpenAPI generation.
Example
>>> config = OpenAPIConfig( ... title="My API", ... version="2.0.0", ... description="API for my application" ... )
- class uf.Preset(name, func_name, params, description='')[source]#
Bases:
objectA saved parameter preset for a function.
- name#
Preset name
- func_name#
Function this preset is for
- params#
Parameter values
- description#
Optional description
- created_at#
When preset was created
- class uf.PresetManager[source]#
Bases:
objectManage parameter presets for functions.
Example
>>> presets = PresetManager() >>> presets.save('quick_add', 'add', {'x': 10, 'y': 20}, 'Quick test') >>> preset = presets.get('quick_add', 'add') >>> result = my_func(**preset.params)
- classmethod from_dict(data)[source]#
Create from dictionary.
- Parameters:
data (
dict) – Dictionary with preset data- Return type:
- Returns:
PresetManager instance
- class uf.ResultRenderer[source]#
Bases:
objectBase class for result renderers.
Subclass this to create custom renderers for specific result types.
- class uf.ResultRendererRegistry[source]#
Bases:
objectRegistry for result renderers.
Manages a collection of renderers and selects the appropriate one for each result type.
- register(renderer, priority=0)[source]#
Register a renderer.
- Parameters:
renderer (
ResultRenderer) – ResultRenderer instancepriority (
int) – Higher priority renderers are tried first
- Return type:
- register_for_type(result_type, renderer)[source]#
Register a renderer for a specific type.
- Parameters:
result_type (
Type) – Python type to matchrenderer (
ResultRenderer) – ResultRenderer instance
- Return type:
- class uf.RjsfConfigBuilder[source]#
Bases:
objectBuilder for RJSF configurations with sensible defaults.
This class helps construct RJSF specifications by providing a fluent interface for configuring fields.
Example
>>> builder = RjsfConfigBuilder() >>> builder.field('email', RjsfFieldConfig(format='email')) >>> builder.field('message', RjsfFieldConfig(widget='textarea')) >>> spec = builder.build(base_schema)
- class_names(class_names)[source]#
Set CSS class names for the form.
- Parameters:
class_names (
str) – Space-separated CSS class names- Return type:
- Returns:
Self for method chaining
- field(param_name, config)[source]#
Configure a specific field.
- Parameters:
param_name (
str) – Name of the parameter/fieldconfig (
RjsfFieldConfig) – Configuration for the field
- Return type:
- Returns:
Self for method chaining
- class uf.RjsfFieldConfig(widget=None, ui_options=<factory>, format=None, enum=None, description=None, placeholder=None, title=None, disabled=False, readonly=False, hidden=False, default=None)[source]#
Bases:
objectConfiguration for individual form fields.
This class allows fine-grained control over how form fields are rendered in the RJSF interface.
- widget#
Widget type (e.g., ‘textarea’, ‘select’, ‘radio’, ‘date’)
- ui_options#
Additional UI options for the widget
- format#
JSON Schema format (e.g., ‘email’, ‘uri’, ‘date-time’)
- enum#
List of allowed values (for dropdowns)
- description#
Field description/help text
- placeholder#
Placeholder text for the input
- title#
Custom title for the field
- disabled#
Whether the field is disabled
- readonly#
Whether the field is read-only
Whether to hide the field
- default#
Default value for the field
Example
>>> email_config = RjsfFieldConfig( ... widget='email', ... format='email', ... placeholder='user@example.com' ... )
- class uf.SessionManager(secret_key, session_timeout=3600)[source]#
Bases:
objectManage user sessions.
Example
>>> sessions = SessionManager(secret_key='my-secret') >>> session_id = sessions.create_session('admin') >>> user = sessions.get_session(session_id)
- cleanup_expired()[source]#
Remove expired sessions.
- Return type:
- Returns:
Number of sessions removed
- class uf.Task(func, args=(), kwargs=None, task_id=None)[source]#
Bases:
objectRepresents a background task.
- task_id#
Unique task identifier
- func_name#
Name of the function
- args#
Positional arguments
- kwargs#
Keyword arguments
- status#
Current task status
- result#
Task result (if completed)
- error#
Error message (if failed)
- created_at#
When task was created
- started_at#
When task started running
- completed_at#
When task completed
- progress#
Progress percentage (0-100)
- class uf.TaskQueue(num_workers=1, max_queue_size=100)[source]#
Bases:
objectFIFO queue for background tasks.
Example
>>> task_queue = TaskQueue(num_workers=2) >>> task_queue.start() >>> task_id = task_queue.submit(expensive_function, x=10, y=20) >>> status = task_queue.get_status(task_id) >>> result = task_queue.get_result(task_id)
- get_result(task_id, wait=False, timeout=None)[source]#
Get task result.
- Parameters:
- Return type:
- Returns:
Task result
- Raises:
ValueError – If task not found
RuntimeError – If task failed
TimeoutError – If wait times out
- get_status(task_id)[source]#
Get task status.
- Parameters:
task_id (
str) – Task ID- Return type:
- Returns:
TaskStatus or None if not found
- class uf.Theme(name, colors=<factory>, fonts=<factory>, css='')[source]#
Bases:
objectUI theme configuration.
- name#
Theme name
- colors#
Color palette
- fonts#
Font configuration
- css#
Additional custom CSS
- class uf.ThemeConfig(default_theme='light', allow_toggle=True, available_themes=None, custom_theme=None)[source]#
Bases:
objectConfiguration for theme system.
Example
>>> config = ThemeConfig( ... default_theme='dark', ... allow_toggle=True, ... available_themes=['light', 'dark', 'ocean'] ... )
- class uf.UfApp(funcs, **mk_rjsf_app_kwargs)[source]#
Bases:
objectWrapper class for uf applications.
Provides a higher-level interface with additional conveniences beyond the raw qh app.
- app#
The underlying qh/bottle/fastapi app
- function_specs#
FunctionSpecStore for function metadata
- funcs#
Dictionary mapping function names to callables
- class uf.UfAppTester(app)[source]#
Bases:
objectContext manager for testing uf apps.
Provides a testing context with common utilities and assertions.
Example
>>> with UfAppTester(app) as tester: ... result = tester.submit_form('add', {'x': 10, 'y': 20}) ... tester.assert_success(result) ... tester.assert_result_equals(result, 30)
- assert_error_type(result, error_type)[source]#
Assert that error type matches.
- Parameters:
- Raises:
AssertionError – If error types don’t match
- assert_failure(result, message='')[source]#
Assert that a result indicates failure.
- Parameters:
- Raises:
AssertionError – If result is successful
- assert_result_equals(result, expected)[source]#
Assert that result value equals expected.
- Parameters:
- Raises:
AssertionError – If values don’t match
- assert_success(result, message='')[source]#
Assert that a result indicates success.
- Parameters:
- Raises:
AssertionError – If result is not successful
- class uf.UfTestClient(app)[source]#
Bases:
objectTest client for uf applications.
Provides a convenient interface for testing uf apps without running a web server.
Example
>>> from uf import mk_rjsf_app >>> app = mk_rjsf_app([my_func]) >>> client = UfTestClient(app) >>> response = client.call_function('my_func', {'x': 10, 'y': 20}) >>> assert response['success']
- call_function(func_name, params, *, expect_success=True)[source]#
Call a function with the given parameters.
- Parameters:
- Return type:
- Returns:
Result dictionary with ‘success’ and ‘result’ or ‘error’
- Raises:
AssertionError – If expect_success=True and call fails
- class uf.User(username, password_hash, roles=<factory>, permissions=<factory>, metadata=<factory>, created_at=<factory>, is_active=True)[source]#
Bases:
objectUser account information.
- username#
Unique username
- password_hash#
Hashed password
- roles#
List of role names
- permissions#
List of permission strings
- metadata#
Additional user metadata
- created_at#
When user was created
- is_active#
Whether account is active
- class uf.WebhookClient(url, headers=None, timeout=10.0, retry_count=3)[source]#
Bases:
objectClient for sending webhooks.
Example
>>> client = WebhookClient('https://example.com/webhook') >>> client.send(WebhookEvent('success', 'my_func', {}, result=42))
- send(event, async_send=True)[source]#
Send a webhook event.
- Parameters:
event (
WebhookEvent) – WebhookEvent to sendasync_send (
bool) – Whether to send asynchronously
- Return type:
- Returns:
True if sent successfully (for sync sends)
- class uf.WebhookEvent(event_type, func_name, params, result=None, error=None)[source]#
Bases:
objectRepresents a webhook event.
- event_type#
Type of event (‘success’, ‘failure’, ‘start’)
- func_name#
Name of the function
- params#
Function parameters
- result#
Function result (if success)
- error#
Error message (if failure)
- timestamp#
When event occurred
- class uf.WebhookManager[source]#
Bases:
objectManage webhooks for multiple URLs and events.
Example
>>> manager = WebhookManager() >>> manager.add_webhook('https://example.com/hook1', events=['success']) >>> manager.add_webhook('https://example.com/hook2', events=['failure']) >>> manager.trigger('success', 'my_func', {}, result=42)
- uf.add_openapi_routes(app, funcs, **spec_kwargs)[source]#
Add OpenAPI routes to an app.
Adds:
/openapi.json: OpenAPI specification
/docs: Swagger UI
/redoc: ReDoc UI
- uf.apply_field_configs(schema, ui_schema, field_configs)[source]#
Apply field configurations to existing schemas.
- Parameters:
schema (
dict) – JSON Schema to modifyui_schema (
dict) – UI Schema to modifyfield_configs (
dict[str,RjsfFieldConfig]) – Mapping of field names to configurations
- Return type:
- Returns:
Tuple of (modified_schema, modified_ui_schema)
Example
>>> configs = { ... 'email': get_field_config('email'), ... 'bio': get_field_config('multiline_text'), ... } >>> schema, ui_schema = apply_field_configs(schema, ui_schema, configs)
- uf.assert_field_required(spec, field_name)[source]#
Assert that a field is required.
- Parameters:
- Raises:
AssertionError – If field is not required
- uf.assert_field_type(spec, field_name, expected_type)[source]#
Assert that a field has the expected type.
- Parameters:
- Raises:
AssertionError – If type doesn’t match
- uf.assert_has_field(spec, field_name)[source]#
Assert that a spec has a specific field.
- Parameters:
- Raises:
AssertionError – If field not found
- uf.assert_valid_rjsf_spec(spec)[source]#
Assert that a specification is valid RJSF format.
- Parameters:
spec (
dict) – The specification to validate- Raises:
AssertionError – If spec is invalid
- uf.async_to_sync(async_func)[source]#
Convert an async function to a synchronous wrapper.
- Parameters:
async_func (
Callable) – Async function to wrap- Return type:
- Returns:
Synchronous wrapper function
Example
>>> async def fetch_data(url: str): ... # async operations ... return data >>> sync_fetch = async_to_sync(fetch_data) >>> result = sync_fetch('https://example.com')
- uf.auto_group_by_module(funcs)[source]#
Automatically group functions by their module.
- Parameters:
- Return type:
- Returns:
FunctionOrganizer with module-based groups (a
functools.partialgoes with the module of the function it wraps)
Example
>>> from myapp import user_ops, report_ops >>> funcs = [user_ops.create, user_ops.delete, report_ops.generate] >>> organizer = auto_group_by_module(funcs)
- uf.auto_group_by_prefix(funcs, separator='_')[source]#
Automatically group functions by name prefix.
Groups functions based on the part of their name before the separator. For example, with separator=”_”:
user_create, user_delete → “user” group
report_generate, report_export → “report” group
- Parameters:
- Return type:
- Returns:
FunctionOrganizer with auto-generated groups
Example
>>> funcs = [user_create, user_delete, report_generate, admin_reset] >>> organizer = auto_group_by_prefix(funcs) >>> # Creates groups: 'user', 'report', 'admin'
- uf.auto_group_by_tag(funcs, tag_attr='__uf_group__')[source]#
Automatically group functions by a tag attribute.
Functions can be tagged with a group name using an attribute.
- Parameters:
- Return type:
- Returns:
FunctionOrganizer with tag-based groups
Example
>>> def create_user(name: str): ... pass >>> create_user.__uf_group__ = 'Admin' >>> >>> organizer = auto_group_by_tag([create_user, other_func])
- uf.background(queue_name='default', task_queue=None)[source]#
Decorator to run function in background.
- Parameters:
- Returns:
Decorator function
Example
>>> @background() ... def send_email(to: str, subject: str): ... # Long-running email sending ... pass >>> >>> task_id = send_email('user@example.com', 'Hello') >>> # Returns immediately with task_id
- uf.cached(ttl=3600, backend=None, key_func=None)[source]#
Decorator to cache function results.
- Parameters:
ttl (
int) – Time to live in secondsbackend (
Optional[CacheBackend]) – Cache backend (uses global MemoryCache if None)key_func (
Optional[Callable]) – Optional function to generate cache key
- Returns:
Decorator function
Example
>>> @cached(ttl=3600) ... def expensive_calculation(x: int, y: int) -> int: ... # Only runs once per unique (x, y) combination ... return heavy_computation(x, y) >>> >>> result = expensive_calculation(10, 20) # Computes >>> result2 = expensive_calculation(10, 20) # From cache
- uf.deprecated(message=None)[source]#
Decorator to mark a function as deprecated.
Example
>>> @deprecated("Use new_function instead") ... def old_function(): ... pass
- uf.enable_history(func, max_size=100)[source]#
Decorator to enable call history for a function.
Example
>>> @enable_history ... def my_function(x: int): ... return x * 2
- uf.field_config(**field_configs)[source]#
Decorator to configure specific fields of a function.
- Parameters:
**field_configs (
RjsfFieldConfig) – Keyword arguments mapping parameter names to configs- Returns:
Decorator function
Example
>>> from uf.rjsf_config import get_field_config >>> >>> @field_config( ... email=get_field_config('email'), ... bio=get_field_config('multiline_text') ... ) ... def create_profile(email: str, bio: str): ... pass
- uf.generate_openapi_spec(funcs, title='API', version='1.0.0', description='', servers=None)[source]#
Generate OpenAPI 3.0 specification.
- uf.get_example(func)[source]#
Get example arguments from a function.
- Parameters:
func (
Callable) – Function to get example from- Return type:
- Returns:
Tuple of (args, kwargs) or None if no example
Example
>>> example = get_example(my_function) >>> if example: ... args, kwargs = example ... result = my_function(*args, **kwargs)
- uf.get_field_config(config_name)[source]#
Get a predefined field configuration by name.
- Parameters:
config_name (
str) – Name of the configuration (e.g., ‘email’, ‘multiline_text’)- Return type:
- Returns:
RjsfFieldConfig instance
- Raises:
KeyError – If config_name not found
Example
>>> email_config = get_field_config('email') >>> email_config.format 'email'
- uf.get_field_configs(func)[source]#
Get field configurations from a function.
- Parameters:
func (
Callable) – Function to get field configs from- Return type:
- Returns:
Dictionary mapping parameter names to RjsfFieldConfig
Example
>>> field_configs = get_field_configs(my_function) >>> if 'email' in field_configs: ... print(field_configs['email'].format)
- uf.get_field_dependencies(func)[source]#
Get field dependencies from a function.
- Parameters:
func (
Callable) – Function to get dependencies from- Return type:
- Returns:
List of FieldDependency objects
- uf.get_global_history_manager()[source]#
Get the global history manager.
- Return type:
- Returns:
Global HistoryManager instance
- uf.get_global_registry()[source]#
Get the global transformation registry.
- Return type:
- Returns:
The global InputTransformRegistry instance
- uf.get_global_renderer_registry()[source]#
Get the global renderer registry.
- Return type:
- Returns:
The global ResultRendererRegistry instance
- uf.get_group(func)[source]#
Get group name from a function.
- Parameters:
func (
Callable) – Function to get group from- Return type:
- Returns:
Group name or None
Example
>>> group = get_group(my_function) >>> if group: ... print(f"Function is in group: {group}")
- uf.get_theme(name)[source]#
Get a built-in theme by name.
Example
>>> theme = get_theme('dark') >>> css = theme.to_css()
- uf.get_ui_config(func)[source]#
Get UI configuration from a function.
- Parameters:
func (
Callable) – Function to get config from- Return type:
- Returns:
Configuration dictionary or None if not configured
Example
>>> config = get_ui_config(my_function) >>> if config: ... print(config['title'])
- uf.group(group_name)[source]#
Decorator to assign a function to a group.
Simpler alternative to ui_config when you only need to set the group.
- Parameters:
group_name (
str) – Name of the group- Returns:
Decorator function
Example
>>> @group("Admin") ... def delete_user(user_id: int): ... pass
Decorator to hide a function from the UI.
The function will still be callable via the API but won’t appear in the UI navigation.
Example
>>> @hidden ... def internal_function(): ... pass
- uf.is_async_function(func)[source]#
Check if a function is async.
- Parameters:
func (
Callable) – Function to check- Return type:
- Returns:
True if function is async
Example
>>> async def my_async_func(): ... pass >>> is_async_function(my_async_func) True
Check if a function is hidden from UI.
- Parameters:
func (
Callable) – Function to check- Return type:
- Returns:
True if function is hidden, False otherwise
Example
>>> if not is_hidden(my_function): ... # Show in UI ... pass
- uf.make_sync_compatible(funcs)[source]#
Convert any async functions in the list to sync wrappers.
- Parameters:
funcs (
list[Callable]) – List of functions (may include async)- Return type:
- Returns:
List of functions (all synchronous)
Example
>>> funcs = [sync_func, async_func, another_sync] >>> compatible = make_sync_compatible(funcs) >>> # All functions in compatible are now callable synchronously
- uf.mk_grouped_app(groups, **mk_rjsf_app_kwargs)[source]#
Create a uf app with grouped function navigation.
- Parameters:
groups (
Iterable[FunctionGroup]) – Iterable of FunctionGroup objects**mk_rjsf_app_kwargs – Arguments passed to mk_rjsf_app
- Returns:
Configured web application with grouped navigation
Example
>>> admin_group = FunctionGroup('Admin', [user_create, user_delete]) >>> reports_group = FunctionGroup('Reports', [generate_report]) >>> app = mk_grouped_app([admin_group, reports_group])
- uf.mk_rjsf_app(funcs, *, config=None, input_trans=None, output_trans=None, rjsf_config=None, ui_schema_factory=None, param_to_prop_type=None, page_title='Function Interface', function_display_names=None, custom_css=None, rjsf_theme='default', add_ui=True, **qh_kwargs)[source]#
Create an RJSF-backed web app from a list of functions.
This is the main entry point for uf. It combines qh’s HTTP service generation with ju’s RJSF form generation to create a complete web interface for your functions.
- Parameters:
funcs (
Union[Iterable[Callable],Mapping[str,Callable]]) – Iterable of callables to expose via web UI – functions, partials, callable instances (named byuf.specs.name_of_func) – or a mapping{name: callable}to name them yourself. The name is the route and the key of the form specconfig (
Optional[Any]) – Optional qh.AppConfig for HTTP service configurationinput_trans (
Optional[Callable]) – Optional input transformation function for qhoutput_trans (
Optional[Callable]) – Optional output transformation function for qhrjsf_config (
Optional[dict]) – Optional configuration dict for RJSF generationui_schema_factory (
Optional[Callable]) – Optional callable to customize UI schemaparam_to_prop_type (
Optional[Callable]) – Optional callable to map parameters to typespage_title (
str) – Title for the web interfacefunction_display_names (
Optional[Mapping]) – Optional mapping to override function namescustom_css (
Optional[str]) – Optional custom CSS for the web interfacerjsf_theme (
str) – RJSF theme to use (‘default’, ‘material-ui’, etc.)add_ui (
bool) – Whether to add UI routes (set False for API-only)**qh_kwargs – Additional keyword arguments passed to qh.mk_app
- Returns:
HTTP endpoints for each function
RJSF-based web interface (if add_ui=True)
API routes for function specs
- Return type:
A configured web application (bottle or FastAPI app) with
Example
>>> def add(x: int, y: int) -> int: ... '''Add two numbers''' ... return x + y ... >>> def greet(name: str) -> str: ... '''Greet a person''' ... return f"Hello, {name}!" ... >>> app = mk_rjsf_app([add, greet]) >>> # app.run() # Start the web server
Example with customization:
>>> from uf import mk_rjsf_app, RjsfFieldConfig >>> >>> def send_email(to: str, subject: str, body: str): ... '''Send an email''' ... pass ... >>> app = mk_rjsf_app( ... [send_email], ... page_title="Email Sender", ... custom_css="body { background: #f0f0f0; }", ... )
- uf.pydantic_model_to_json_schema(model_class)[source]#
Convert a Pydantic model to JSON Schema.
- Parameters:
model_class – Pydantic model class
- Return type:
- Returns:
JSON Schema dictionary
Example
>>> from pydantic import BaseModel >>> class User(BaseModel): ... name: str ... age: int >>> schema = pydantic_model_to_json_schema(User)
- uf.rate_limit(calls, period)[source]#
Decorator to mark a function with rate limiting metadata.
This is metadata-only; actual rate limiting must be implemented separately.
- Parameters:
- Returns:
Decorator function
Example
>>> @rate_limit(calls=10, period=60) # 10 calls per minute ... def send_email(to: str, subject: str): ... pass
- uf.register_renderer(renderer, priority=0)[source]#
Register a renderer in the global registry.
- Parameters:
renderer (
ResultRenderer) – ResultRenderer instancepriority (
int) – Higher priority renderers are tried first
- Return type:
- uf.register_type(*args, **kwargs)[source]#
Register a type in the global registry.
This is a convenience function that uses the global registry. See InputTransformRegistry.register_type for full documentation.
- uf.require_auth(backend, roles=None, permissions=None)[source]#
Decorator to require authentication for a function.
- Parameters:
- Returns:
Decorator function
Example
>>> backend = DictAuthBackend.from_dict({ ... 'admin': {'password': 'secret', 'roles': ['admin']} ... }) >>> @require_auth(backend, roles=['admin']) ... def delete_all(): ... pass
- uf.requires_auth(*, roles=None, permissions=None)[source]#
Decorator to mark a function as requiring authentication.
This is metadata-only; actual authentication must be implemented separately in the application layer.
- Parameters:
- Returns:
Decorator function
Example
>>> @requires_auth(roles=['admin'], permissions=['user:delete']) ... def delete_user(user_id: int): ... pass
- uf.result_renderer(renderer_type)[source]#
Decorator to specify result renderer for a function.
- Parameters:
renderer_type (
str) – Type of renderer to use- Returns:
Decorator function
Example
>>> @result_renderer('table') ... def get_users() -> list[dict]: ... return [{'name': 'Alice', 'age': 30}]
- uf.retry_async(max_attempts=3, delay=1.0)[source]#
Decorator to add retry logic to async functions.
- Parameters:
- Returns:
Decorator function
Example
>>> @retry_async(max_attempts=3, delay=2.0) ... async def unreliable_api_call(): ... # May fail, will retry ... return await external_api.fetch()
- uf.test_ui_function(func, test_inputs, *, expected_output=None, expected_exception=None)[source]#
Test a function with form-like input.
- Parameters:
- Return type:
- Returns:
True if test passes
- Raises:
AssertionError – If test fails
Example
>>> def add(x: int, y: int) -> int: ... return x + y >>> test_ui_function(add, {'x': 10, 'y': 20}, expected_output=30) True
- uf.timeout_async(seconds)[source]#
Decorator to add timeout to async functions.
- Parameters:
seconds (
float) – Timeout in seconds- Returns:
Decorator function
Example
>>> @timeout_async(5.0) ... async def slow_operation(): ... await asyncio.sleep(10) # Will timeout after 5s
- uf.ui_config(*, title=None, description=None, group=None, fields=None, hidden=False, icon=None, order=None)[source]#
Decorator to add UI configuration to functions.
This decorator attaches metadata to functions that uf can use to customize the generated UI.
- Parameters:
- Returns:
Decorator function
Example
>>> @ui_config( ... title="User Registration", ... group="Admin", ... fields={'email': RjsfFieldConfig(format='email')} ... ) ... def register_user(email: str, name: str): ... pass
- uf.webhook(on=None, url=None, manager=None)[source]#
Decorator to add webhooks to a function.
- Parameters:
- Returns:
Decorator function
Example
>>> @webhook(on=['success', 'failure']) ... def process_order(order_id: int): ... # Process order ... return {'status': 'processed'}
- uf.with_dependencies(*dependencies)[source]#
Decorator to attach field dependencies to a function.
- Parameters:
*dependencies (
FieldDependency) – FieldDependency objects- Returns:
Decorator function
Example
>>> @with_dependencies( ... FieldDependency('reason', 'other_reason', ... lambda v: v == 'other', ... DependencyAction.SHOW) ... ) ... def submit_feedback(reason: str, other_reason: str = ''): ... pass
- uf.with_example(*example_args, **example_kwargs)[source]#
Decorator to attach example arguments to a function.
This can be used to provide example/test data that appears in the UI.
- Parameters:
*example_args – Example positional arguments
**example_kwargs – Example keyword arguments
- Returns:
Decorator function
Example
>>> @with_example(x=10, y=20) ... def add(x: int, y: int) -> int: ... return x + y
- uf.wrap_pydantic_function(func)[source]#
Wrap a function that uses Pydantic models.
The wrapper converts dict inputs to Pydantic models before calling the function, and converts Pydantic outputs back to dicts.
Example
>>> from pydantic import BaseModel >>> class User(BaseModel): ... name: str >>> def create_user(user: User) -> User: ... return user >>> wrapped = wrap_pydantic_function(create_user) >>> result = wrapped({'name': 'Alice'}) # Pass dict, not User
Modules
Async function support for uf. |
|
Authentication and authorization for uf. |
|
Core functionality for uf - UI Fast. |
|
Result caching for uf. |
|
UI metadata decorators for uf. |
|
Field dependencies and interactions for uf. |
|
Call history and presets for uf. |
|
OpenAPI/Swagger integration for uf. |
|
Import optional dependencies without masking broken installs. |
|
Function grouping and organization for uf. |
|
Pydantic integration for uf. |
|
Result rendering system for uf. |
|
RJSF customization layer for uf. |
|
API routes for uf web interface. |
|
Function specification management for uf. |
|
HTML template generation for uf web interface. |
|
Testing utilities for uf applications. |
|
Theme and styling support for uf. |
|
Input transformation integration for uf. |
|
Webhook support for uf. |