Tempfile Module Implementation Plan
Overview
Create a Fennel tempfile module that mirrors Python's tempfile functionality, providing secure creation of temporary files and directories with automatic cleanup via drop methods.
Dependencies
New: lua_random.cpp (C++ sol2 binding)
A general-purpose random module similar to Python's random, required for generating secure temporary file names.
API:
(local random (require :random))
;; Seeding
(random.seed n) ; seed with integer
(random.seed) ; seed from system entropy (std::random_device)
;; Integer generation
(random.randint a b) ; random integer in [a, b] inclusive
(random.randrange stop) ; random integer in [0, stop)
(random.randrange start stop) ; random integer in [start, stop)
(random.randrange start stop step) ; random integer in range with step
;; Float generation
(random.random) ; random float in [0.0, 1.0)
(random.uniform a b) ; random float in [a, b]
;; Bytes
(random.randbytes n) ; n random bytes as raw string
(random.randbytes-hex n) ; n random bytes as hex string (2n chars)
;; Sequence operations
(random.choice seq) ; random element from sequence (table)
(random.shuffle seq) ; shuffle sequence in-place, returns it
(random.sample seq k) ; k unique random elements (new table)Implementation notes:
- Use
std::mt19937_64as the PRNG engine - Seed from
std::random_deviceby default for non-deterministic behavior - Use
std::uniform_int_distributionandstd::uniform_real_distribution - Thread-local generator to avoid contention
randbytesuses the engine directly, extracting bytes from 64-bit outputs
Existing: fs module
Already provides all needed filesystem operations:
fs.exists,fs.create-dir,fs.create-dirsfs.remove,fs.remove-allfs.join-path,fs.write-filefs.stat
Existing: appdirs module
Provides cross-platform temp directory:
appdirs.tmp-dirreturns system temp directory
Fennel Module: assets/lua/tempfile.fnl
API
(local tempfile (require :tempfile))
;; Get temp directory
(tempfile.gettempdir) ; returns system temp dir (via appdirs)
;; Low-level creation (returns path only, no automatic cleanup)
(tempfile.mkstemp) ; create temp file, returns path
(tempfile.mkstemp {:suffix ".txt" :prefix "data_" :dir "/custom/tmp"})
(tempfile.mkdtemp) ; create temp directory, returns path
(tempfile.mkdtemp {:suffix "_work" :prefix "build_" :dir "/custom/tmp"})
;; High-level handles (automatic cleanup via drop)
(tempfile.NamedTemporaryFile) ; returns handle with :path and :drop
(tempfile.NamedTemporaryFile {:suffix ".log" :prefix "app_" :dir d :delete true})
;; handle.path - the file path
;; handle:drop() - deletes file if :delete is true (default)
(tempfile.TemporaryDirectory) ; returns handle with :path and :drop
(tempfile.TemporaryDirectory {:suffix "" :prefix "tmp" :dir d})
;; handle.path - the directory path
;; handle:drop() - recursively deletes directoryDefaults
prefix:"tmp"suffix:""dir:(appdirs.tmp-dir)delete:true(for NamedTemporaryFile)
Name Generation
Generate names using: {prefix}{random_hex}{suffix}
Where random_hex is 16 hex characters (8 random bytes) from random.randbytes-hex.
For collision avoidance, retry up to 10 times if path exists.
File Structure
src/
lua_random.cpp ; new C++ binding
assets/lua/
tempfile.fnl ; new Fennel module
assets/lua/tests/
test-random.fnl ; tests for random module
test-tempfile.fnl ; tests for tempfile moduleImplementation Steps
Phase 1: C++ Random Module
Create
src/lua_random.cpp:- Include
<random>,<string>,<algorithm> - Thread-local
std::mt19937_64engine - Implement all API functions
- Register via
lua_bind_randominlua_runtime.cpp
- Include
Add declaration and call in
src/lua_runtime.cpp:cppvoid lua_bind_random(sol::state&); // ... lua_bind_random(lua);Build and verify:
make build
Phase 2: Random Module Tests
Create assets/lua/tests/test-random.fnl:
- Test
seedproduces reproducible sequences - Test
randintbounds are inclusive - Test
randrangebounds and step - Test
randomanduniformproduce floats in range - Test
randbytesandrandbytes-hexlength - Test
choicereturns element from table - Test
shufflemodifies in place - Test
samplereturns correct count without duplicates
Phase 3: Fennel Tempfile Module
Create assets/lua/tempfile.fnl:
- Require
random,fs,appdirs - Implement
gettempdir - Implement
mkstempandmkdtempwith retry logic - Implement
NamedTemporaryFilereturning handle table - Implement
TemporaryDirectoryreturning handle table
Phase 4: Tempfile Module Tests
Create assets/lua/tests/test-tempfile.fnl:
- Test
gettempdirreturns non-empty string - Test
mkstempcreates file that exists - Test
mkdtempcreates directory that exists - Test
NamedTemporaryFilepath exists, drop deletes it - Test
NamedTemporaryFilewithdelete=falsepreserves file - Test
TemporaryDirectorypath exists as dir, drop removes it - Test custom prefix/suffix/dir options
- Test collision retry (mock or stress test)
Phase 5: Register Tests
Add new test modules to assets/lua/tests/fast.fnl.
Error Handling
- Throw errors (via
error()orassert) for:- Failed file/directory creation after retries exhausted
- Invalid arguments (negative byte counts, empty sequences for choice)
- Do not silently fail or return nil
Thread Safety
- C++ random module uses thread-local storage for the PRNG
- Fennel tempfile module is not thread-safe (Lua single-threaded)
Validation
After implementation:
make buildsucceedsSKIP_KEYRING_TESTS=1 XDG_DATA_HOME=/tmp/space/tests/xdg-data SPACE_DISABLE_AUDIO=1 SPACE_ASSETS_PATH=$(pwd)/assets make testpasses- Manual verification of temp file creation and cleanup
