{"id":13725228,"url":"https://github.com/giannitedesco/minotaur","last_synced_at":"2026-03-01T11:03:36.785Z","repository":{"id":57441861,"uuid":"289624576","full_name":"giannitedesco/minotaur","owner":"giannitedesco","description":"A pythonic, asynchronous, inotify interface","archived":false,"fork":false,"pushed_at":"2025-10-28T11:33:16.000Z","size":37,"stargazers_count":173,"open_issues_count":2,"forks_count":3,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-11-12T14:31:37.631Z","etag":null,"topics":["asynchronous","asynchronous-programming","asyncio","inotify","python","python-asyncio","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/giannitedesco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-08-23T05:47:06.000Z","updated_at":"2025-10-28T11:33:07.000Z","dependencies_parsed_at":"2023-01-21T16:16:28.184Z","dependency_job_id":null,"html_url":"https://github.com/giannitedesco/minotaur","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/giannitedesco/minotaur","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giannitedesco%2Fminotaur","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giannitedesco%2Fminotaur/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giannitedesco%2Fminotaur/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giannitedesco%2Fminotaur/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/giannitedesco","download_url":"https://codeload.github.com/giannitedesco/minotaur/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giannitedesco%2Fminotaur/sbom","scorecard":{"id":426116,"data":{"date":"2025-08-11","repo":{"name":"github.com/giannitedesco/minotaur","commit":"c254a5406886b57df387fa89e3c388d30c2305e2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 2/29 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T02:17:30.383Z","repository_id":57441861,"created_at":"2025-08-19T02:17:30.384Z","updated_at":"2025-08-19T02:17:30.384Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29967932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T10:55:55.490Z","status":"ssl_error","status_checked_at":"2026-03-01T10:55:55.175Z","response_time":124,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","asynchronous-programming","asyncio","inotify","python","python-asyncio","python3"],"created_at":"2024-08-03T01:02:16.363Z","updated_at":"2026-03-01T11:03:36.770Z","avatar_url":"https://github.com/giannitedesco.png","language":"Python","readme":"# Minotaur: A pythonic, asynchronous, inotify interface\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://img.shields.io/pypi/v/minotaur?label=pypi\" alt=\"PyPI version\"\u003e\n\u003c/div\u003e\n\n\n## Examples\n\nMinotaur provides the `Inotify` class which is to be used as a context\nmanager, from within which, one may iterate over inotify events:\n\n```python\n    with Inotify() as n:\n        n.add_watch('.', Mask.CREATE | Mask.DELETE | Mask.MOVE)\n        for evt in n:\n            print(evt)\n```\n\nThe asynchronous interface works almost identically. The inotify object must\nbe created in nonblocking mode, and then the mere addition of the `async`\nkeyword to the iteration over events is all that's required:\n\n```python\n    with Inotify(blocking=False) as n:\n        n.add_watch('.', Mask.CREATE | Mask.DELETE | Mask.MOVE)\n        async for evt in n:\n            print(evt)\n```\n\nExample output would look like this:\n\n```python\nEvent(wd=1, mask=\u003cMask.CREATE: 256\u003e, cookie=0, name=PosixPath('foo'))\nEvent(wd=1, mask=\u003cMask.CREATE: 256\u003e, cookie=0, name=PosixPath('bar'))\nEvent(wd=1, mask=\u003cMask.MOVED_FROM: 64\u003e, cookie=129399, name=PosixPath('foo'))\nEvent(wd=1, mask=\u003cMask.MOVED_TO: 128\u003e, cookie=129399, name=PosixPath('baz'))\nEvent(wd=1, mask=\u003cMask.DELETE: 512\u003e, cookie=0, name=PosixPath('bar'))\nEvent(wd=1, mask=\u003cMask.DELETE: 512\u003e, cookie=0, name=PosixPath('baz'))\n\n```\n\nThere is also a command-line tool demonstrating the features\n```bash\n$ python -m minotaur --help\n\nusage: minotaur [-h] [--async | --sync] [--fancy] [--mask MASK] dir [dir ...]\n\nMinotaur: A pythonic, asynchronous, inotify interface.\n\nA summary of inotify watch flags:\n - ACCESS: File was accessed\n - ATTRIB: Metaata changed, eg. permissions\n - CLOSE_WRITE: File for writing was closed\n - CLOSE_NOWRITE: File or dir not opened for writing was closed\n - CREATE: File/dir was created\n - DELETE: File or dir was deleted\n - DELETE_SELF: Watched file/dir was itself deleted\n - MODIFY: File was modified\n - MOVE_SELF: Watched file/dir was itself moved\n - MOVED_FROM: Generated for dir containing old filename when a file is renamed\n - MOVED_TO: Generated for dir containing new filename when a file is renamed\n - OPEN: File or dir was opened\n - MOVE: MOVED_FROM | MOVED_TO\n - CLOSE: IN_CLOSE_WRITE | IN_CLOSE_NOWRITE\n - DONT_FOLLOW: Don't dereference pathname if it is a symbolic link\n - EXCL_UNLINK: Don't generate events after files have been unlinked\n - ONESHOT: Only generate one event for this watch\n - ONLYDIR: Watch pathname only if it is a dir\n - MASK_CREATE: Only watch path if it isn't already being watched\n\npositional arguments:\n  dir                   Watch for events in given dir\n\noptional arguments:\n  -h, --help            show this help message and exit\n  --async, -a           Use asyncio event loop\n  --sync, -s            Use synchronous interface\n  --fancy, -f           Use fancy interface\n  --mask MASK, -m MASK  Events to watch for\n\n```\n\n## What is different about Minotaur?\n\n1. C interface provides basic wrapper to syscalls and constants. In future, if\n   performance becomes a problem, more functionality can be gradually moved\n   there.\n\n2. Pythonic. `IntFlags` is used for watch types. Context-managers take care of\n   fd lifetime, `close()` method is idempotent. Raw `read()` and `readall()`\n   methods work comparably to python standard `io` objects. Full support for\n   `mypy`, including typeshed for C interface. Iterator and async-iterator\n   protocols supported.\n\n3. Makes no assumptions about the name encoding of filesystems, ie. with\n   `os.fsencode()` and `os.fsdecode()`\n\n4. Async interface supports multiple concurrent waiters. Waiting tasks are\n   woken in a first-come, first-serve manner.\n\n5. Users can choose between different levels of support:\n   1. Raw syscall interface\n   2. Low-level inotify object, which takes care of path encoding, reading of\n\traw inotify data, parsing of binary events in to python objects, and\n\tprovides both synchronous and async interface. But is still low-level\n\tbecause it does no special handling of watches or combining of related\n\tevents (eg.`MOVE_FROM` / `MOVE_TO`).\n   3. Fancy high-level interfaces, in pure python, built on top of low-level\n        interface.\n\n## What is missing\n\nThere is no attempt to abstract file-notification functionality offered by\nother operating systems in to a cross-platform interface.\n\nThere are no tests.\n\n## Development\nYou should use the provided pre-commit hooks to make sure code type-checks and\nis PEP-8 formatted:\n\n```bash\nln -sf ../../pre-commit.sh .git/hooks/pre-commit\n```\n\n## Why another one?\n\nThere are several other python inotify packages. So why does this one exist?\nWell, this can perhaps be explained best by referring to some of the others:\n\n1. `PyInotify`: suffers from numerous bugs. The fd closes aren't idempotent,\n   this can lead to closing unrelated file descriptors. This would be less of\n   an issue if the fd had a clear ownership and lifetime, or used context\n   managers. In other words, it's difficult to use safely.\n\n2. `PyInotify`: Assumes utf-8 filesystem encoding. No `asyncio` interface.\n\n3. `inotify_simple`: Nicely subclasses `FileIO`, but that precludes `asyncio`\n   since `FileIO` is meant for blocking I/O on files and cannot be easily\n   adapted for other purposes.\n\n4. `python_inotify`: No `asyncio` interface and, it would need to be added in\n   the C code, or if added in python code would duplicate the C code and work\n   differently, thus being a new API.\n\n5. `python-inotify`: It's packaged by RedHat but, similarly to\n   `python_inotify` the read() syscall is done in the C extension so it\n   doesn't support `asyncio`, and can't easily be adapted to do so without\n   changing the interface or duplicating functionality.\n\n6. `asyncinotify`: Easily the best of the bunch. The main downside is that it\n   doesn't provide a synchronous interface or low-level interfaces.\n\nThe others seem to be parts of larger projects, or systems.\n","funding_links":[],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiannitedesco%2Fminotaur","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgiannitedesco%2Fminotaur","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiannitedesco%2Fminotaur/lists"}