Core Modules¶
Everything below is importable from the top-level suthing package.
File Handling¶
Read and write files whose format is inferred from the file name.
Supported formats are listed in :class:FileType; the extension → format map
is :data:EXTENSIONS. Any of them can be compressed with .gz, .bz2,
.xz or .zst (see :mod:suthing.fs).
FileHandle
¶
Format-aware file I/O: one call to read or write a whole file.
The format comes from the extension (:data:EXTENSIONS) unless how is
given, and a compression suffix is handled transparently. An unknown
extension is an error rather than a guess.
Formats read to / write from:
- YAML, JSON (
.jsonldtoo): any YAML/JSON value. YAML loads withsafe_load; values json cannot serialise go through :func:suthing.to_jsonable. - JSONL: a list of values, one per line.
- CSV, TSV: a pandas
DataFrame(extra keyword arguments go toread_csv/to_csv). - TXT: a
str. - ENV: a
dict[str, str]; loading does not touchos.environ(use :func:suthing.load_envfor that). - PICKLE: any picklable object. Only unpickle files you trust.
Example
data = FileHandle.load("config.yaml") FileHandle.dump(data, "out/config.json.gz", mkdir=True)
Source code in suthing/file_handle.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
dump(item, path, *, how=None, atomic=True, mkdir=False, **kwargs)
classmethod
¶
Write item to a file, compressing by suffix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
Any
|
Value to write (see the class docstring for accepted types). |
required |
path
|
PathLike
|
Destination; |
required |
how
|
FileType | str | None
|
Format override; by default inferred from the extension. |
None
|
atomic
|
bool
|
Replace path in one step, so readers never see a
partial file (see :func: |
True
|
mkdir
|
bool
|
Create missing parent directories first. |
False
|
**kwargs
|
Any
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
Path
|
The path written. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the format cannot be inferred. |
TypeError
|
If item does not fit the format, or for keyword arguments the format does not take. |
Source code in suthing/file_handle.py
iter(path, *, how=None, chunksize=10000, **kwargs)
classmethod
¶
Stream a file instead of loading it whole.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
File to read. |
required |
how
|
FileType | str | None
|
Format override; by default inferred from the extension. |
None
|
chunksize
|
int
|
Rows per |
10000
|
**kwargs
|
Any
|
Passed to |
{}
|
Yields:
| Name | Type | Description |
|---|---|---|
JSONL |
Any
|
one value per line. CSV/TSV: |
TXT |
Any
|
lines without their trailing newline. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For a format that cannot be streamed. |
Source code in suthing/file_handle.py
load(path=None, *, how=None, fpath=None, **kwargs)
classmethod
¶
Read a file from disk.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike | None
|
File to read; |
None
|
how
|
FileType | str | None
|
Format override; by default inferred from the extension. |
None
|
fpath
|
PathLike | None
|
Deprecated spelling of path, kept so code written for
suthing 0.5 keeps working; emits |
None
|
**kwargs
|
Any
|
Passed to |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The parsed contents (see the class docstring for types). |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the format cannot be inferred. |
TypeError
|
For keyword arguments the format does not take, or when neither or both of path and fpath are given. |
Source code in suthing/file_handle.py
load_resource(package, name, *, how=None, **kwargs)
classmethod
¶
Read a data file shipped inside an importable package.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
package
|
str
|
Dotted package name, e.g. |
required |
name
|
str
|
File name relative to the package; may contain |
required |
how
|
FileType | str | None
|
Format override; by default inferred from the extension. |
None
|
**kwargs
|
Any
|
As for :meth: |
{}
|
Returns:
| Type | Description |
|---|---|
Any
|
The parsed contents. |
Source code in suthing/file_handle.py
FileType
¶
Bases: str, Enum
Formats :class:FileHandle reads and writes.
Source code in suthing/file_handle.py
detect_format(path)
¶
Infer the format and compression of path from its name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
File path or name. |
required |
Returns:
| Type | Description |
|---|---|
FileType | None
|
|
str | None
|
extension and |
Source code in suthing/file_handle.py
JSON Lines¶
JSON Lines (.jsonl / .ndjson): one JSON value per line.
All functions accept compressed files (.jsonl.gz etc., see :mod:suthing.fs).
Blank lines are skipped on read.
JsonlError
¶
Bases: NamedTuple
A line that could not be read.
Attributes:
| Name | Type | Description |
|---|---|---|
line |
int
|
1-based line number. |
message |
str
|
What was wrong with it. |
Source code in suthing/jsonl.py
encode_jsonl_row(row)
¶
Encode one value as a UTF-8 JSON line, newline included.
Non-ASCII text is written as-is and values json cannot serialise go through
:func:suthing.to_jsonable.
Source code in suthing/jsonl.py
iter_jsonl(path, *, strict=True, require_object=False)
¶
Stream the values of a JSON Lines file without loading it whole.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
File to read; may be compressed. |
required |
strict
|
bool
|
Raise on a bad line. When |
True
|
require_object
|
bool
|
Treat any value that is not a JSON object as a bad line. |
False
|
Yields:
| Type | Description |
|---|---|
Any
|
One parsed value per non-blank line. |
Raises:
| Type | Description |
|---|---|
ValueError
|
On a bad line when strict is set; the message carries the line number. |
Source code in suthing/jsonl.py
iter_jsonl_stream(stream, *, strict=True, require_object=False)
¶
Parse JSON Lines from an open binary stream; see :func:iter_jsonl.
Source code in suthing/jsonl.py
read_jsonl(path, *, require_object=False)
¶
Read a whole JSON Lines file, collecting bad lines instead of raising.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
File to read; may be compressed. |
required |
require_object
|
bool
|
Treat any value that is not a JSON object as a bad line. |
False
|
Returns:
| Type | Description |
|---|---|
list[Any]
|
|
list[JsonlError]
|
|
Source code in suthing/jsonl.py
write_jsonl(rows, path, *, append=False, atomic=True, mkdir=False)
¶
Write rows as JSON Lines, compressing by suffix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rows
|
Iterable[Any]
|
Values to write, one per line; any iterable, consumed once. |
required |
path
|
PathLike
|
Destination file. |
required |
append
|
bool
|
Add to the end of an existing file instead of replacing it. A compressed file gains a new compressed member, which every reader of that format accepts. |
False
|
atomic
|
bool
|
Replace path in one step (see :func: |
True
|
mkdir
|
bool
|
Create missing parent directories first. |
False
|
Returns:
| Type | Description |
|---|---|
int
|
The number of rows written. |
Source code in suthing/jsonl.py
write_jsonl_stream(rows, stream)
¶
Write rows to an open binary stream; returns the number written.
Source code in suthing/jsonl.py
File System¶
File-system primitives: path expansion, transparent compression, atomic writes.
Compression is chosen from the file name: .gz, .bz2 and .xz use the
standard library; .zst needs the optional zstandard package
(pip install suthing[zstd]).
atomic_open(path, *, mkdir=False, durable=False)
¶
Open a binary stream whose contents replace path only on success.
Data goes to a temporary file in the same directory, which is moved over
path with :func:os.replace when the block exits without an exception.
Readers therefore see either the old file or the complete new one, and
concurrent writers never interleave (the last one to finish wins). On an
exception the temporary file is removed and path is left untouched.
The new file keeps the permissions of the file it replaces, or gets the
usual 0o666 & ~umask if path did not exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
Destination file; |
required |
mkdir
|
bool
|
Create missing parent directories first. |
False
|
durable
|
bool
|
|
False
|
Yields:
| Type | Description |
|---|---|
IO[bytes]
|
A binary file object to write to. |
Source code in suthing/fs.py
atomic_write(path, data, *, encoding='utf-8', mkdir=False, durable=False)
¶
Write data to path atomically (see :func:atomic_open).
The data is written as-is: a .gz name is not compressed. Use
:meth:suthing.FileHandle.dump for format- and compression-aware writes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
Destination file. |
required |
data
|
bytes | str
|
Bytes, or text encoded with encoding. |
required |
encoding
|
str
|
Encoding for text data. |
'utf-8'
|
mkdir
|
bool
|
Create missing parent directories first. |
False
|
durable
|
bool
|
|
False
|
Returns:
| Type | Description |
|---|---|
Path
|
The destination path. |
Source code in suthing/fs.py
expand_path(path)
¶
Expand ~ and make path absolute, resolving symlinks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
Path as a string or path-like object. |
required |
Returns:
| Type | Description |
|---|---|
Path
|
The resolved absolute path. |
Source code in suthing/fs.py
open_compressed(path, mode='rb')
¶
Open path as a binary stream, decompressing or compressing by suffix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
File to open; |
required |
mode
|
str
|
One of |
'rb'
|
Yields:
| Type | Description |
|---|---|
IO[bytes]
|
A binary file object. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If mode is not a binary read/write/append mode. |
Source code in suthing/fs.py
split_compression(path)
¶
Split a file name into its format suffix and compression suffix.
The format suffix is the last extension once any compression suffix is
removed. A dotfile such as .env counts as its own suffix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
File path or name. |
required |
Returns:
| Type | Description |
|---|---|
str
|
|
str | None
|
is |
tuple[str, str | None]
|
an uncompressed name. |
Example
split_compression("data/rows.jsonl.gz") ('.jsonl', '.gz') split_compression("config/.env") ('.env', None)
Source code in suthing/fs.py
wrap_compressed(fileobj, compression, mode)
¶
Wrap an open binary file object in a (de)compressing stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fileobj
|
IO[bytes]
|
Underlying binary file object. |
required |
compression
|
str
|
A key of :data: |
required |
mode
|
str
|
|
required |
Returns:
| Type | Description |
|---|---|
IO[bytes]
|
A binary stream over fileobj; close it before closing fileobj so |
IO[bytes]
|
compressed trailers get flushed. |
Source code in suthing/fs.py
Timing¶
Wall-clock timing and timestamps.
Timer
¶
Bases: ContextDecorator
Measure the wall-clock time of a block or a function.
Uses :func:time.perf_counter. elapsed is live inside the block and
frozen once it exits. On exit the timer reports itself to log if given,
otherwise logs at DEBUG level when it has a label.
As a decorator (@Timer("load")) the same instance is re-entered on
every call, so it reports the most recent call; it is not safe for
recursive or concurrent calls of the decorated function.
Example
with Timer() as t: ... do_work() print(t.elapsed_str) with Timer("ingest", log=print): ... ingest() # prints "ingest: 1.23 sec" on exit
Source code in suthing/timer.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
elapsed
property
¶
Seconds since entering; 0.0 if the timer never started.
elapsed_ms
property
¶
:attr:elapsed in whole milliseconds.
elapsed_str
property
¶
:attr:elapsed formatted with two decimals, e.g. "2 min 7.12 sec".
mins
property
¶
Whole minutes of :attr:elapsed. Deprecated: use :attr:elapsed.
running
property
¶
Whether the timer has started and not yet exited.
secs
property
¶
Whole seconds past :attr:mins. Deprecated: use :attr:elapsed.
__init__(label=None, log=None)
¶
Create a timer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str | None
|
Name used when reporting on exit. |
None
|
log
|
Callable[[str], object] | None
|
Called with |
None
|
Source code in suthing/timer.py
format_duration(seconds, digits=2)
¶
Format a duration as "1 min 30.5 sec" (minutes only when non-zero).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seconds
|
float
|
Duration in seconds. |
required |
digits
|
int
|
Decimal places for the seconds part. |
2
|
Returns:
| Type | Description |
|---|---|
str
|
The formatted duration. |
Source code in suthing/timer.py
utc_now_iso(timespec='seconds')
¶
Current UTC time as ISO 8601 text, e.g. "2026-09-22T10:15:00+00:00".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timespec
|
str
|
Precision, as for :meth: |
'seconds'
|
Returns:
| Type | Description |
|---|---|
str
|
The timestamp, with an explicit |
Source code in suthing/timer.py
Profiling¶
Opt-in function profiling.
Decorate functions with :func:profiled; they are timed only while a
:class:Profiler is active, and cost one context-variable lookup otherwise::
@profiled(key_args="batch_size")
def ingest(rows, batch_size=100): ...
with Profiler() as prof:
ingest(rows, batch_size=50)
ingest(rows, batch_size=500)
prof.summary() # {"ingest(batch_size=50)": ProfileStats(...), ...}
The active profiler lives in a :class:contextvars.ContextVar, so it follows
async tasks. Threads start with no active profiler unless they run in a
copied context (contextvars.copy_context().run).
ProfileStats
¶
Profiler
¶
Collects timings recorded by :func:profiled functions while active.
Source code in suthing/profiling.py
active_profiler()
¶
profiled(func=None, /, *, key_args=(), name=None)
¶
Time calls to a function whenever a :class:Profiler is active.
Usable bare (@profiled) or with options (@profiled(key_args="x")).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[P, R] | None
|
Function to wrap (bare use). |
None
|
key_args
|
str | Sequence[str]
|
Parameter name(s) whose values become part of the key, so
calls with different arguments are reported separately
( |
()
|
name
|
str | None
|
Key prefix; defaults to the function's qualified name. |
None
|
Returns:
| Type | Description |
|---|---|
Callable[P, R] | Callable[[Callable[P, R]], Callable[P, R]]
|
The wrapped function, or a decorator. |
Raises:
| Type | Description |
|---|---|
ValueError
|
At decoration time, if a key_args name is not a parameter of the function. |
Source code in suthing/profiling.py
Comparison¶
Deep comparison of nested data, with the location of every difference.
Difference
¶
Bases: NamedTuple
One place where two structures differ.
Attributes:
| Name | Type | Description |
|---|---|---|
path |
str
|
Where, e.g. |
expected |
Any
|
The value on the expected side, or :data: |
actual |
Any
|
The value on the actual side, or :data: |
reason |
str
|
Short description of the mismatch. |
Source code in suthing/compare.py
diff(expected, actual, *, rel_tol=0.0, abs_tol=0.0, ignore_order=False, max_diffs=None)
¶
List every difference between two nested structures.
Mappings are compared key by key; sets as sets; other non-string iterables
(lists, tuples, generators, arrays) item by item, so a length mismatch is
reported as missing or unexpected items. Everything else is compared with
==. Numbers of different types compare by value, nan equals
nan, and bool is not treated as a number.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expected
|
Any
|
Reference value. |
required |
actual
|
Any
|
Value under test. |
required |
rel_tol
|
float
|
Relative tolerance for numbers (:func: |
0.0
|
abs_tol
|
float
|
Absolute tolerance for numbers. |
0.0
|
ignore_order
|
bool
|
Match sequence items regardless of position (as a multiset). Quadratic in sequence length. |
False
|
max_diffs
|
int | None
|
Stop after this many differences. |
None
|
Returns:
| Type | Description |
|---|---|
list[Difference]
|
The differences, empty when the structures match. |
Example
Source code in suthing/compare.py
equals(a, b, *, rel_tol=0.0, abs_tol=0.0, ignore_order=False)
¶
Whether two nested structures match; see :func:diff for the rules.
Stops at the first difference.
Source code in suthing/compare.py
Hashing¶
Stable content hashes: of JSON-like values, text, bytes, files and directory trees.
Every function takes an algorithm name understood by :func:hashlib.new
("sha256" by default) and returns a hex digest.
bytes_hash(data, *, length=None, algorithm='sha256')
¶
Hex digest of data, optionally truncated to length characters.
canonical_json(obj, *, default=None)
¶
Render obj as canonical JSON: sorted keys, no whitespace, ASCII-escaped.
Equal values give identical text regardless of dict insertion order, which makes the result suitable for hashing and for use as a cache key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
JSON-serialisable value. |
required |
default
|
Callable[[Any], Any] | None
|
Hook for values json cannot serialise (e.g.
:func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The canonical JSON text. |
Source code in suthing/hashing.py
file_hash(path, *, length=None, algorithm='sha256')
¶
Hex digest of a file's raw bytes, read in chunks.
Source code in suthing/hashing.py
stable_hash(obj, *, length=None, algorithm='sha256', default=None)
¶
Hex digest of :func:canonical_json of obj.
Equal to hashlib.sha256(json.dumps(obj, sort_keys=True,
separators=(",", ":")).encode("utf-8")).hexdigest(), so it can replace
that expression without changing any stored hash.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
JSON-serialisable value. |
required |
length
|
int | None
|
Keep only the first length hex characters. |
None
|
algorithm
|
str
|
:mod: |
'sha256'
|
default
|
Callable[[Any], Any] | None
|
Passed to :func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The hex digest. |
Source code in suthing/hashing.py
text_hash(text, *, length=None, algorithm='sha256', encoding='utf-8')
¶
Hex digest of text encoded with encoding, optionally truncated.
Source code in suthing/hashing.py
tree_hash(root, *, pattern='*', length=None, algorithm='sha256')
¶
Hex digest of every file under root and its path relative to root.
Files are visited in sorted order of their POSIX relative paths, so the result does not depend on the file system's listing order. Renaming or moving a file changes the hash; so does changing its contents. Directories and symlinks to directories contribute only through the files they hold.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
PathLike
|
Directory to hash. |
required |
pattern
|
str
|
Glob matched recursively against file names ( |
'*'
|
length
|
int | None
|
Keep only the first length hex characters. |
None
|
algorithm
|
str
|
:mod: |
'sha256'
|
Returns:
| Type | Description |
|---|---|
str
|
The hex digest. |
Raises:
| Type | Description |
|---|---|
NotADirectoryError
|
If root is not a directory. |
Source code in suthing/hashing.py
JSON Conversion¶
Coerce Python values into what :func:json.dumps accepts.
to_jsonable(obj, *, nan=None)
¶
Recursively convert obj into JSON-serialisable built-in types.
Conversions:
dict/mapping →dictwithstrkeys;list,tuple,setandfrozenset→list(sets are sorted when they can be, so the output is deterministic)Enum→ itsvalue; subclasses ofstr/int/float→ the plain built-indatetime/date/time→ ISO 8601 text;timedelta→ secondsDecimal→float;UUIDand paths →str- dataclass instances →
dictof their fields - numpy scalars and arrays → Python scalars and nested lists (numpy is only consulted when it is already imported)
- non-finite floats (
nan,inf) → nan, since JSON has no such value
Can also be passed as default= to :func:json.dumps, where it is called
only for values json cannot serialise itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
obj
|
Any
|
Value to convert. |
required |
nan
|
Any
|
Replacement for non-finite floats. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A structure of |
Any
|
|
Raises:
| Type | Description |
|---|---|
TypeError
|
For a value with no known conversion. |
Source code in suthing/jsonable.py
Iteration¶
Iteration helpers.
batched(iterable, n)
¶
Split iterable into consecutive lists of n items; the last may be shorter.
Works on any iterable, including generators, and consumes it lazily.
Unlike :func:itertools.batched (Python 3.12+) it yields lists, which
callers can index, extend or pass straight to bulk-insert APIs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
iterable
|
Iterable[T]
|
Items to split. |
required |
n
|
int
|
Batch size. |
required |
Yields:
| Type | Description |
|---|---|
list[T]
|
Lists of at most n items. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If n is less than 1. |
Example
list(batched(range(5), 2)) [[0, 1], [2, 3], [4]]
Source code in suthing/iterx.py
Text¶
Text helpers.
slugify(text, *, sep='-', fallback='item', lower=False, ascii_fold=False, max_length=None)
¶
Turn text into a token safe for file names and URL path segments.
Every run of characters outside A-Za-z0-9._- becomes one sep, and
sep is stripped from both ends.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
sep
|
str
|
Replacement for each run of unsafe characters. |
'-'
|
fallback
|
str
|
Returned when nothing safe is left. |
'item'
|
lower
|
bool
|
Lower-case the result. |
False
|
ascii_fold
|
bool
|
Replace accented letters by their base letter ( |
False
|
max_length
|
int | None
|
Cut the result to at most this many characters, then strip sep again. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The slug, or fallback. |
Example
slugify(" Person / Company ") 'Person-Company' slugify("Café Menü", sep="_", lower=True, ascii_fold=True) 'cafe_menu'
Source code in suthing/text.py
Environment¶
Environment variables: boolean flags and .env files.
env_flag(name, default=False)
¶
Read environment variable name as a boolean.
1/true/t/yes/y/on are true and 0/false/f/no/n/off are false,
ignoring case and surrounding whitespace. Unset or empty gives default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Variable name. |
required |
default
|
bool
|
Value when the variable is unset or empty. |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
The flag value. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For any other value, so a typo does not silently read as default. |
Source code in suthing/environ.py
load_env(path, *, override=False)
¶
Load a dotenv file into :data:os.environ.
To read a dotenv file without touching the environment, use
FileHandle.load(path), which returns its values as a dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
PathLike
|
Dotenv file. |
required |
override
|
bool
|
Replace variables that are already set. By default the existing environment wins. |
False
|
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
The variables the file defines (with values |
dict[str, str]
|
dropped). |
Source code in suthing/environ.py
Logging¶
One-call logging configuration for scripts and CLIs.
setup_logging(level='INFO', *, config=None, fmt=DEFAULT_FORMAT, stream=None, force=False)
¶
Configure the root logger, from a config file or with sensible defaults.
With config, the file decides everything: .conf/.ini files go to
:func:logging.config.fileConfig and .yaml/.yml/.json files
to :func:logging.config.dictConfig. Loggers that already exist are kept
enabled in both cases. Without config, :func:logging.basicConfig is
called with level, fmt and stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
level
|
int | str
|
Root level, as a number or a name such as |
'INFO'
|
config
|
PathLike | None
|
Optional logging config file. |
None
|
fmt
|
str
|
Format string when no config is given. |
DEFAULT_FORMAT
|
stream
|
TextIO | None
|
Output stream when no config is given (default |
None
|
force
|
bool
|
Replace handlers already attached to the root logger. |
False
|
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If config does not exist. |
ValueError
|
If config has an unsupported extension. |