Configuration#
Environment Variables#
The below environment variables directly affect what this library does, mostly in its console, and by extension, the command line:
AUTILSCFGPATH- Absolute path to a configuration fileFORCE_COLOR- Force coloured output to be used; overridesTERM=dumbbut emits a warning, since this is probably not meantAttention
FORCE_COLOR,NO_COLORandTERM=dumbcontrol both the argument parser and the PyREPL console.NO_COLOR- Force coloured output to be disabled; overridesFORCE_COLORNote
The override is an arbitrary Python convention. It differs in other languages and frameworks.
PYTHON_BASIC_REPL- Use the pre-3.13 REPL if equal to “1”, even if the newer REPL may be availableImportant
The console will set this variable to “1” when it sees TERM=dumb as long as
-Eis not passed to the Python interpreter.
PYTHONSTARTUP- Decode the file here withtokenizeand execute as a Python source file, making its symbols accessible from the consoleTERM- Turn off smart terminal features, including ANSI colour sequences, when set to “dumb”
Note
The argument parser does not consider the PYTHON_COLORS environment variable, but the coloured edition of the console, which uses
_colorize under the hood, may. To avoid this inconsistency, do not use PYTHON_COLORS to customize asyncutils’s coloring.
Arguments to Python that are considered#
Some arguments consumed by the Python interpreter are also taken into account by the library:
-E- Omit the execution ofPYTHONSTARTUPin the console namespace and the query ofPYTHON_BASIC_REPL-I- Implies-E(sys.flagsenforces this relationship out-of-the-box; documented here for completeness)-i- Always make the console interactive even if standard input is not a TTYWarning
A piped standard input will cause deadlocks or fail for most shells, and this flag may make it worse. It is thus declared experimental and unstable; you might see this anomaly vanish after a single patch version, or in a commit that does not bump the patch.
-q- To the REPL,python -qis equivalent toasyncutils -q
Tip
Even if python -S is used, which indeed does not load site as normal, the exit, quit, help, copyright, credits and
license commands will still work as normal in the console since they are implemented natively, albeit with the help of _sitebuiltins.
This is attributable to a PyREPL quirk or feature. However, accessing them in any fashion other than a bare statement will cause NameError
to be thrown. There is also a clear command to clear the terminal screen that will fail similarly, but that is not related to -S.
See also
- The Python command line and environment variables
authoritative source from the official Python docs
Basic Customization#
An extensible, two-part configuration system is in place. The first part is static/frozen, detailed below.
It includes aspects such as where to output logging, customizing the underlying executor type used, and setting a seed for random number generation
using the AUTILSCFGPATH environment variable.
AUTILSCFGPATH is read at the first import of this library, and the configuration is loaded and applied immediately. Errors will be thrown as
appropriate if the file is not found or contains values of the incorrect type, after the library tries its best to coerce the types, but you may see
raw ModuleNotFoundError’s if the library cannot be located or executed.
Automatic discovery of config files, as in other libraries or command-line tools, is not supported, because there is no standard location for it and determining a precedence for the different allowed file extensions would be arbitrary, non-trivial and difficult to maintain.
The options are shown below, along with their default values and descriptions:
/* This file describes the intended format of the json files whose paths can be set as AUTILSCFGPATH to configure the behaviour of this module.
The values associated with the keys are their defaults.
Some keys can have various types, as detailed by the comments attached. */
{
logging_to: "STDERR",
// string file path or integer file descriptor, to which the logging is dumped
// corresponds to -l/--log-to
no_log: false,
// disable logging; conflicts with logging_to if true
// corresponds to -n/--no-log
V: 0, // increase logging verbosity; corresponds to -V
Q: 0, // decrease logging verbosity; corresponds to -Q
// V and Q are not respected when running the console
executor: "thread",
/* choose the PEP 3148 executor class to use when required
refer to `tools.get_cmd_help()` for possible values
corresponds to -e/--executor/-c/--custom-executor */
quiet: false,
// ask the console not to show introductory and closing text
// corresponds to -q/--quiet
basic_repl: false,
// run the console without any _pyrepl wrapping; not recommended
// corresponds to -b/--basic-repl
max_memerrs: 3,
// protect resource integrity by exiting the console on the MemoryError at the index after this
// corresponds to -m/--max-memerrs
debug: false,
// turn on debug mode by entering the global debug context manager
// incurs performance penalty due to excessive logging, so do not use outside testing
// corresponds to -d/--debug
load_all: false,
// load all submodules when the console starts; corresponds to -p/--load-all
seed: null,
// string, integer, float or bytestring literal to seed the module-global random device
// corresponds to -s/--seed
pdb: false,
// whether to bring the user into the pdb debugger on SystemExit with non-zero status from the console; corresponds to -P/--pdb
next_config: null,
/* string file path to the json to be used for the next time this module is to be configured
This key may be useful for temporary configurations that want to be automatically unset and replaced with a main configuration.
If null, the environment variable is unset entirely after reading this config.
Cycles can be created to switch between flags periodically.
Ensure AUTILSCFGPATH is set to a valid file path if you keep encountering errors, since it could have been incorrectly set
in a previous config. By that logic, you should also remember to update this correctly when the config file is moved. */
context: { // configures individual submodules (contextual constants)
/* Essentially maps submodule names to objects like {utility1: {key1: value1, key2: value2, ...}}, utility2: {...}};
or if the utility has only one configurable parameter, there will be a key at the top level of a mapping above,
with the keys being sorted alphabetically. */
altlocks: {
circuit_breaker: {
default_max_fails: 3,
default_max_half_open_calls: 5,
default_reset: 30.0,
},
dynamic_throttle: {
default_jitter: 0.2,
default_lbound: 0.25,
default_lfactor: 0.75,
default_max_rate: 100.0,
default_min_rate: 1e-2,
default_ubound: 0.75,
default_ufactor: 1.25,
default_window: 100,
}
},
base: {
event_loop_base_flags: 0,
aiter_to_gen: {
default_allow_futures: true,
default_strict: false
},
iter_to_agen: {
default_may_create_executor: false,
default_strict: false,
default_use_existing_executor: false
}
},
buckets: {
token_bucket_default_consume_tokens: 1.0,
leaky_bucket: {
adjmap: [
[256, [0.15, 1.1, 0.85, 0.9]],
[128, [0.23, 1.2, 0.77, 0.81]],
[0, [0.3, 1.4, 0.7, 0.73]]
],
default_acquire_tokens: 1.0,
default_ext_can_set_factor: true,
default_max_factor: 10.0,
default_min_factor: 0.1,
default_wait_for_tokens_tokens: 1.0,
wait_for_tokens_tick: 0.1
}
},
caches: {
async_lru_cache_default_maxsize: 128,
background_refresh_cache: {
default_refresh: 15.0,
default_ttl: 60.0
}
},
channels: {
event_bus: {
default_max_concurrent: 64,
publish_default_safe: true,
stream_default_buffer_size: 100,
stream_default_item_timeout: 3.0,
stream_default_timeout: 5.0
},
observable_default_ntimes_n: 1,
rendezvous_maintenance_interval: 30.0
},
compete: {convert_to_coro_iter_default_skip_invalid: true},
events: {
event_with_value: {
default_max_hist: 128,
default_recent: 5.0
}
},
func: {
benchmark: {
default_times: 3,
default_warmup: 0
},
retry: {
default_backoff: 2.0,
default_delay: 0.5,
default_jitter: 0.2,
default_max_delay: 30.0,
default_tries: 3
},
timer_default_precision: 7
},
io: {
memory_mapped_io_manager: {
default_checksum_alg: "blake2s",
default_minimize_writes: true
}
},
iters: {
afrievalds_default_k: 2,
aonlinesorter_default_slow: false,
aunzip: {
default_max_qsize: 64,
default_put_batch: 16
},
merge_default_max_qsize: 0,
tee: {
default_put_exc: true,
default_max_qsize: 32
}
},
locks: {
advanced_rate_limit_default_tokens: 1.0,
dynamic_bounded_semaphore_default_value: 1,
locksmith_default_timeouts: [1.0, 0.1, null],
priority_semaphore_default_value: 1
},
misc: {gather_with_limited_concurrency_default_max_concurrent: 8},
networking: {
line_protocol_default_buffer_size: 4096,
socket_transport_limits: [2048, 8192]
},
pools: {
advanced_pool: {
default_max_workers: 5,
default_min_workers: 1,
threshold_hi: 1.5,
threshold_lo: 0.5
},
connection_pool: {
default_max_life: 3600.0,
default_max_size: 10,
default_min_size: 1,
maintenance_interval: 30.0
}
},
processors: {
batch_processor: {
default_max_size: 128,
default_max_time: 1.0
},
bounded_batch_processor: {
default_batch_size: 10,
default_max_concurrent: 5
},
bulkhead: {
default_max_queue: 32,
default_max_rej: -1
}
},
queues: {
password_queue: {
default_get_from: "password",
default_put_from: "password"
}
},
rwlocks: {rwlock_default_prefer_writers: true},
signals: {wait_for_signals_default_signals: [2, 15]},
util: {
dual_context_manager: {
default_may_create_executor: false,
default_strict: false,
default_use_existing_executor: true
},
semaphore_default_value: 1
}
}
}
New options will likely be added in the future, but every current option is considered stable and has a corresponding default value.
The above keys have a near one-to-one correspondence with the command line arguments, as the comments below each key explain. Use asyncutils -?
to see detailed CLI usage.
The config file can be written in the below formats, listed with the third-party libraries they require if any:
Format |
File extension |
PyPI package name |
Module name |
|---|---|---|---|
JSON |
.json |
json |
|
TOML |
.toml |
tomllib |
|
YAML |
.yaml, .yml |
PyYAML |
yaml |
JSON5 |
.json5 |
json5 |
json5 |
JSONC |
.jsonc |
json-with-comments |
jsonc |
Hjson |
.hjson |
hjson |
hjson |
XML |
.xml |
xmltodict |
xmltodict |
Danger
Many implementations used are subject to certain attacks related to crafting of input leading to quadratic complexity or worse. Be especially careful with XML. It is verbose, overkill and not recommeneded for use, especially with many simpler alternatives. In any case, write your configs yourself to avoid malicious inputs exhausting computing resources.
See also
- CVE-2025-9375
a vulnerability of the
XMLGeneratorclass from the standard library used byxmltodictwithout input sanitizationNote
This exploit is disputed by the maintainers of the project.
- the CVE database
for any new vulnerabilities
Important
To write the config in each format, adhere to the exact analog of the nested dictionary structure shown in format.json5 in the chosen language.
Warning
Though the exact parsing method used by this module may allow object nesting deviating from that shown, you should still strictly adhere to it.
Tip
To ensure all supported formats can be parsed, install the pconf extra.
INI is not supported because it is outdated and lacks strong typing, meaning all values are interpreted as strings.
It is currently possible to associate file extensions not shown above with other libraries providing a load() function taking a file object and
returning a dictionary, by modifying the asyncutils._internal.unparsed.D map from file extensions to names of corresponding modules. However,
it is believed that the options offered are versatile enough to fit every individual need, so this functionality is just a minor trait of the
implementation that just so happens to have been declared stable.
Contextual “Constants”#
You can see that the json also includes many submodule names as keys; this is the second, contextual part of the configuration. It is thread-safe,
async-safe and mutable, thanks to contextvars. The sheer magnitude of options makes them infeasible to include as command line arguments.
By convention, they are called contextual constants since no code in this library is expected to change their values, only reading from them to determine things from dynamic default arguments to frequencies of background tasks and internal thresholds.
One may find it useful to alter the context dynamically without creating a new context. This can be achieved as follows:
asyncutils.getcontext().update( # call the update method of the current context to modify in-place
{'SOCKET_TRANSPORT_LIMITS': (1024, 16384)}, # can optionally pass in a dictionary as the first and only positional argument
ITER_TO_AGEN_DEFAULT_USE_EXISTING_EXECUTOR=True, # fields go here; keyword arguments are accepted
observable_default_ntimes_n=3, # lowercase or mixed-case is allowed but not recommended
lEAky_BUckeT_WaiT_for_toKEnS_tick=0.1, # fields do not have to be in order
WAIT_FOR_SIGNAL_DEFAULT_SIGNALS=[2] # list will be automatically converted into tuple
) # check if a string `name` is a valid field name using `name.upper() in asyncutils.all_contextual_constants`
However, due care must be exercised to avoid messing up other parts of your program relying on this context. It is advisable to call the following methods that leave the original context alone by deriving a new one from it:
__copy__() and __replace__() are also implemented to help copy.copy()
and copy.replace() respectively.
It is even better to use asyncutils.context.nonreusablelocalcontext, which returns a one-time context manager, or the convenience method
ascurctx() on context objects that wraps it.
For more detailed documentation on context usage, see the context page.
Tip
You can think of the Context class as similar to decimal.Context, but with different methods and attributes
and customizing an entire module insteasd of quirks of the operations of a single class.