Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
API design an choice
#1
i am creating a group of functions that have a similar purpose across a list of file paths. i will look at four of them for this question although the answer will be applied more of them later on. these are just examples for the general API question:

1. wait until all files exist
2. wait until any file exists
3. wait until all files do not exist
4. wait until any file does not exist

if these were commands, the way to give such a command a list of files would be to provide them as command line arguments. but since i am making them as Python functions callable by other Python code that has access to their names. one way to design the API is for the files to be provided as individual file arguments called like:

wait_until_all_files_exist(file1,file2,file3)

the other way is to pass a list (or tuple) as a single argument called like:

wait_until_all_files_exist([file1,file2,file3])
wait_until_all_files_exist((file1,file2,file3))

i cannot decide which is best although i am leaning toward a list. that got me to think of allowing either way by testing the type of the first argument. if the first argument is a list (or tuple) then it has all the file names. if the first argument is a string or bytes then all the arguments are individual names. if i include thorough testing, then anything else will raise some more meaningful exception.

i could go more extreme and flatten a list of lists (or tuples) of any depth or even collect all the names from iterators.

what is your opinion on these or other variation of an API that some new functions should use?
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#2
Built-in functions all() and any() take a single iterable argument. That would be my preferred choice for the sake of consistency and simplicity.
« We can solve any problem by introducing an extra level of indirection »
Reply
#3
if my "any"-like and "all"-like functions can take a single iterable argument, does it break anything if they can also take multiple arguments (e.g. args* in the prototype)?
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#4
(Apr-27-2024, 05:26 PM)Skaperen Wrote: does it break anything if they can also take multiple arguments (e.g. args* in the prototype)?
It does not break anything except perhaps future interfaces of your function, but you may think this constraint is not too restrictive
  • You may want to add later other positional arguments (unlikely)
  • You may want to add later keyword arguments, but then you can still add them as keyword-only arguments.
  • You are adding a systematic unnecessary type-checking at execution time.
  • Some automated tool may later try to analyse the code and may fail to do so because of the variable interface.
  • Human readers may find your code harder to read.
  • Simple is better than complex (import this)
« We can solve any problem by introducing an extra level of indirection »
Reply
#5
One suggestion could be like this,so here wait live in a none blocking way using asyncio
import asyncio
from pathlib import Path

async def file_exists(file_path: Path) -> bool:
    """Asynchronously check if a file exists."""
    await asyncio.sleep(0.5)
    return file_path.exists()

async def wait_until_all_files_exist(file_paths: tuple[Path]):
    """Wait until all specified files exist, with periodic updates every 10 seconds."""
    while True:
        results = await asyncio.gather(*(file_exists(fp) for fp in file_paths))
        if all(results):
            print("All files found")
            break
        else:
            # List missing files
            missing_files = [fp for fp, exists in zip(file_paths, results) if not exists]
            print(f"Still missing files: {', '.join(str(mf) for mf in missing_files)}")
        await asyncio.sleep(10)

async def main():
    await wait_until_all_files_exist((Path('file1.txt'), Path('file2.txt'), Path('file3.txt')))

asyncio.run(main())
If run this will work like this,as soon as it make file2.txt and file3.txt in folder will update until all done.
G:\div_code\reader_env
λ python all_files_1.py
Still missing files: file2.txt, file3.txt
Still missing files: file2.txt, file3.txt
Still missing files: file3.txt
Still missing files: file3.txt
Still missing files: file3.txt
Still missing files: file3.txt
All files found
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020