├── .flake8 ├── .gitignore ├── Contributing.md ├── README.md ├── app.py ├── assets └── icon.png ├── config.py ├── core ├── __init__.py ├── controller.py ├── model.py └── views │ ├── __init__.py │ ├── floating_window.py │ ├── styling │ ├── __init__.py │ └── floating_window_style.py │ ├── system_tray.py │ └── text_processing.py ├── example1.gif ├── example2.gif ├── extensions ├── __init__.py ├── basics │ └── __init__.py ├── langraph │ ├── __init__.py │ └── langraph.py ├── ollama │ ├── __init__.py │ └── example.py ├── openai │ ├── __init__.py │ └── utils.py └── variables.py ├── makefile ├── poetry.lock ├── pyproject.toml ├── tests ├── __init__.py └── test_dummy.py └── utils └── __init__.py /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = 4 | .git, 5 | __pycache__, 6 | .venv, 7 | build, 8 | dist, 9 | 10 | # ANN101: Missing type annotation for self in method 11 | # ANN102: Missing type annotation for cls in method 12 | # D400: First line should end with a period for a docstring 13 | # D401: First line should be in imperative mood for a docstring 14 | # ANN003: Missing type annotation for a kwargs argument 15 | 16 | extend-ignore=ANN101,ANN102,D400,D401,ANN003,N802 17 | suppress-none-returning=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | .idea/ 169 | 170 | # Ruff stuff: 171 | .ruff_cache/ 172 | 173 | # PyPI configuration file 174 | .pypirc 175 | 176 | .vscode/ 177 | 178 | .python-version -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Orange Intelligence 2 | 3 | We’re thrilled that you want to contribute to Orange Intelligence! While we don’t have a fully established contribution process at the moment, here’s what it should look like: 4 | 5 | --- 6 | 7 | ## 🛠 Contribution Workflow 8 | 9 | 1. **Open an Issue** 10 | - Start by opening an issue in this repository. 11 | - Use a proper tag to classify your issue: 12 | - `ux`: For UI/UX improvements. 13 | - `bug`: For bugs or unexpected behavior. 14 | - `feature-request`: For new features you'd like to see. 15 | - `extension-function`: For proposing new functions/extensions to be added to the floating window. 16 | - Wait for feedback before diving into implementation. We don’t want you to waste valuable time on something that might not align with the project’s goals. 17 | 18 | 2. **Create a Pull Request (PR)** 19 | - Once your issue is approved, fork the repository and start working on your changes. 20 | - When ready, submit a PR linked to your issue. 21 | 22 | 3. **Make the CI Happy** 23 | - Ensure your code passes all linting and tests. Run these locally before pushing your changes. 24 | 25 | 4. **Celebrate Your Contribution!** 26 | - Once your PR is merged, enjoy the satisfaction of contributing to an open-source project. 🎉 27 | 28 | --- 29 | 30 | ## 🧹 Code Style 31 | 32 | We strive to maintain clean, consistent, and readable code. Please follow these practices: 33 | 34 | - Run linters (`black`, `flake8`) before submitting your PR. 35 | - Include unit tests for new features or fixes where applicable. 36 | 37 | --- 38 | 39 | ## 📝 Notes 40 | 41 | - If you have ideas but aren’t sure how to implement them, feel free to open an issue just to discuss! 42 | - Don’t hesitate to ask questions if you’re stuck—collaboration is key. 43 | 44 | --- 45 | 46 | Thank you for helping make Orange Intelligence better! 🧡 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🍊 Orange Intelligence 2 | 3 | **A Better Open Source Version of Apple Intelligence for macOS** 4 | 5 | Orange Intelligence is a powerful, fully customizable productivity tool for macOS. With its elegant floating window interface, you can capture, process, and replace text seamlessly across any application. Whether you're running basic text processing, leveraging the power of large language models (LLMs) like OpenAI or local LLaMA, or creating complex agent systems, Orange Intelligence empowers you to work smarter, faster, and better. 6 | 7 | --- 8 | 9 | ## 🌟 Why Orange Intelligence? 10 | 11 | Apple Intelligence is closed, limited, and inflexible. Orange Intelligence brings the power of customization and open source innovation to macOS, making it the perfect productivity tool for developers, researchers, and AI enthusiasts. 12 | 13 | --- 14 | ## Demo 15 | 16 | | Feature | Demo GIF | 17 | |----------------------------------|-------------------------------| 18 | | **Variables and text processing examples** | ![alt text](example1.gif) | 19 | | **LLMs with local Ollama** | ![alt text](example2.gif) | 20 | 21 | 22 | ## ✨ Features 23 | 24 | - **Floating Text Processor**: Double-tap the `Option` key to open a sleek floating window. 25 | - **Run Any Python Function**: Execute any Python code, from simple string manipulations to advanced AI/LLM integrations (OpenAI, local LLaMA, or your own agents). 26 | - **Fully Customizable**: Add your own Python logic to extend its functionality and tailor the app to your specific workflows. 27 | - **Global variable replacement**: Global variable replacement (You no longer need to open your notes app to do a simple copy and paste!) 28 | --- 29 | 30 | ## 🛠️ How It Works 31 | 32 | Orange Intelligence simplifies text processing with a three-step pipeline: 33 | 34 | 1. **Capture Text from Any Application** 35 | - Using a clever Applescript trick, the app simulates a global `Cmd+C` to grab the content of the clipboard from the active application. 36 | 37 | 2. **Process the Text** 38 | - The floating window opens in focus, allowing the user to select which Python function to run. The selected function processes the clipboard text (e.g., formatting, AI generation, etc.). 39 | 40 | 3. **Replace the Text** 41 | - Once processed, the floating window closes, the focus returns to the previous app, and a global `Cmd+V` pastes the updated content back into the app. 42 | 43 | --- 44 | 45 | ## 🚀 Getting Started 46 | 47 | ### Prerequisites 48 | 49 | 1. **Install Python 3.9+** 50 | 2. **Install Poetry** for dependency management: 51 | ```bash 52 | pip install poetry 53 | ``` 54 | 3. **Grant Permissions**: Ensure your Python interpreter has the following rights: 55 | - **Accessibility**: To allow replacing text via `Cmd+V`. 56 | - **Input Monitoring**: To listen for global key shortcuts like the `Option` key. 57 | 58 | 4. **Optional Dependencies**: 59 | - **Ollama**: If you want to run local LLaMA models, ensure [Ollama](https://ollama.ai/) is installed and running. 60 | - If you have a custom setup, you might need to adapt the Python code for your environment. 61 | 62 | 5. **Optional Configuration**: 63 | - Adjust the log level in `config.py` if needed. 64 | 65 | ### Installation 66 | 67 | 1. Clone this repository: 68 | ```bash 69 | git clone [https://github.com/sharingan-no-kakashi/orange-intelligence.git] 70 | cd orange-intelligence 71 | ``` 72 | 2. Install dependencies using Poetry: 73 | ```bash 74 | poetry install 75 | ``` 76 | 3. Start the app using Make: 77 | ```bash 78 | make run 79 | ``` 80 | (Use `make help` to see other available commands.) 81 | 82 | 4. When the **orange icon** appears in the system tray, the app is ready to use. 83 | 84 | ### Usage 85 | 86 | 1. Highlight any text in a macOS application. 87 | 2. Double-tap the `Option` key to bring up the floating window. 88 | 3. Select a function to apply to the highlighted text. 89 | 90 | --- 91 | 92 | ## 🧩 Customization 93 | 94 | - Add custom logic by defining callable Python objects in the `extensions` package. 95 | - **Every function (or callable object)** defined in the `__init__.py` file of the `extensions` package will automatically appear as an option in the floating window. 96 | 97 | Example: 98 | ```python 99 | # extensions/__init__.py 100 | def reverse_text(input_text): 101 | return input_text[::-1] 102 | ``` 103 | 104 | --- 105 | 106 | ## 📝 To-Do and Future Improvements 107 | 108 | These are the features currently in progress: 109 | 110 | - **Custom Prompts**: Add the ability to pass custom prompts (hint: that's why you see `**kwargs` in the code). 111 | - **Text Playground**: Open a new custom window for text processing, allowing users to combine LLM/Python/text utilities in a single workspace. 112 | - **Clipboard Restoration**: Automatically restore the content of the clipboard after processing operations. 113 | - **UI/UX Enhancements**: Improve the design and user experience for better usability. 114 | 115 | - **Code improvements**: The codebase feels a bit hacky at the moment, there are lots debatable decisions (subprocessing/time.sleep(), controller feels a bit overwhelmed of logic) etc 116 | - **Other platforms support** There is probably no reason why this cannot be extended to other platforms linux/window. 117 | - **CI/CD** Setup a nice ci pipeline to run the lint/banding/mypy and handling versions/releases 118 | --- 119 | 120 | ## 🏗️ Tech Stack 121 | 122 | - **Python**: The core logic of the app is written in Python for flexibility and extensibility. 123 | - **PyQt6**: Provides a polished and responsive UI for the floating window, built with a clean MVC architecture. 124 | - **Applescript**: Facilitates system-level clipboard interactions and application switching. 125 | 126 | --- 127 | 128 | 129 | ## 📝 License 130 | 131 | This project is licensed under the [MIT License](LICENSE). 132 | 133 | --- 134 | 135 | ## 🤝 Contributing 136 | 137 | We welcome contributions! Check out the [CONTRIBUTING.md](Contributing.md) for guidelines on how to get involved. 138 | 139 | --- 140 | 141 | ## 📧 Contact 142 | 143 | Questions, feedback, or ideas? Reach out via Github issues. 144 | 145 | --- 146 | 147 | **Empower your workflow with 🍊 Orange Intelligence — because better is open source.** 148 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import logging.config 2 | import sys 3 | 4 | from config import CONFIG 5 | from PyQt6.QtWidgets import QApplication 6 | from utils import avoid_dock_macos_icon 7 | 8 | from core.controller import Controller 9 | from core.model import Model 10 | 11 | 12 | def main(): 13 | logging.config.dictConfig(CONFIG["logging"]) 14 | 15 | app = QApplication(sys.argv) 16 | 17 | avoid_dock_macos_icon() 18 | 19 | model = Model() 20 | 21 | controller = Controller(model=model, view=app) 22 | 23 | # Run the event loop 24 | sys.exit(controller.view.exec()) 25 | 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/assets/icon.png -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tempfile 4 | 5 | LOGGING_LEVEL = "DEBUG" 6 | 7 | CONFIG = { 8 | "app": { 9 | "name": "orange-ai", 10 | "icon": "assets/icon.png", 11 | }, 12 | "variables": { 13 | "import_bash_profile": False, 14 | }, 15 | "ollama": { 16 | "name": "ollama", 17 | "url": "https://ollama.com", 18 | "default_model": "llama3.1", 19 | }, 20 | "openai": {"api_key": os.environ.get("OPENAPI_KEY"), "default_model": "gpt-3.5-turbo"}, 21 | "logging": { 22 | "version": 1, 23 | "disable_existing_loggers": False, 24 | "formatters": { 25 | "detailed": {"format": "%(asctime)s - %(levelname)s - %(name)s - %(message)s"}, 26 | }, 27 | "handlers": { 28 | "stream": { 29 | "level": LOGGING_LEVEL, 30 | "class": "logging.StreamHandler", 31 | "stream": sys.stdout, 32 | "formatter": "detailed", 33 | }, 34 | "file": { 35 | "level": LOGGING_LEVEL, 36 | "class": "logging.FileHandler", 37 | "filename": tempfile.NamedTemporaryFile(delete=False).name, 38 | "formatter": "detailed", 39 | }, 40 | }, 41 | "loggers": { 42 | "": { # Root logger configuration 43 | "handlers": ["stream", "file"], 44 | "level": LOGGING_LEVEL, 45 | "propagate": True, 46 | }, 47 | }, 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/core/__init__.py -------------------------------------------------------------------------------- /core/controller.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | 4 | import pyperclip 5 | from pynput import keyboard 6 | from PyQt6.QtCore import QTimer, pyqtSignal 7 | from PyQt6.QtWidgets import QApplication 8 | from utils import cmd_v, get_current_process_id, get_focused_text, put_app_in_focus, return_app_in_focus 9 | 10 | from core.model import Model 11 | from core.views.floating_window import FloatingWindow 12 | from core.views.system_tray import SystemTray 13 | from core.views.text_processing import TextWindow 14 | 15 | LOG = logging.getLogger(__name__) 16 | 17 | 18 | class Controller: 19 | def __init__(self, model: Model, view: QApplication): 20 | self.view = view 21 | self.model = model 22 | self.option_key = False 23 | self.last_time = 0.0 24 | self.floating_window_open = False 25 | self.text_window_open = False 26 | self.focused_process_id = "" 27 | self.focused_text = "" 28 | self.processed_text = "" 29 | self.cmd_pressed = False 30 | self.option_pressed = False 31 | self.this_process_id = get_current_process_id() 32 | self.setup_windows() 33 | self.listener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release) 34 | self.listener.start() 35 | 36 | def get_window_tabs_items(self) -> dict[str, list[str]]: 37 | return self.model.sections 38 | 39 | def update_floating_window_status(self, statue: bool): 40 | self.floating_window_open = statue 41 | 42 | def update_text_window_status(self, status: bool): 43 | self.text_window_open = status 44 | 45 | def setup_event_handlers(self): 46 | self.view.floating_window.custom_signal.connect(self.process_text) 47 | self.view.floating_window.event_put_app_focus.connect(self.put_this_app_in_focus) 48 | self.view.text_window.event_put_app_focus.connect(self.put_this_app_in_focus) 49 | 50 | self.view.floating_window.windows_event.connect(self.update_floating_window_status) 51 | self.view.text_window.windows_event.connect(self.update_text_window_status) 52 | 53 | def recreate_text_window(self): 54 | LOG.debug("Recreating text window") 55 | self.view.text_window = TextWindow( 56 | processing_text=self.focused_text, functions_list=self.model.get_all_functions_flattened() 57 | ) 58 | LOG.debug("Text window recreated") 59 | 60 | def setup_windows(self) -> None: 61 | self.view.main_window = SystemTray() 62 | 63 | # Create the system tray icon 64 | self.view.main_window.show() 65 | 66 | # Create the floating window, passing the key listener to it 67 | tabs = self.get_window_tabs_items() 68 | 69 | self.view.floating_window = FloatingWindow(tabs) 70 | self.recreate_text_window() 71 | self.setup_event_handlers() 72 | 73 | def get_focused_text(self) -> None: 74 | self.focused_text = get_focused_text() 75 | 76 | def return_app_in_focus(self) -> None: 77 | self.focused_process_id = return_app_in_focus() 78 | 79 | def put_previous_app_in_focus(self) -> None: 80 | return put_app_in_focus(self.focused_process_id) 81 | 82 | def put_this_app_in_focus(self) -> None: 83 | return put_app_in_focus(self.this_process_id) 84 | 85 | def set_processed_text(self, section: str, item: str, **kwargs) -> str: 86 | processed_text = self.model.process_text(section, item, self.focused_text, **{}) 87 | pyperclip.copy(processed_text) 88 | self.put_previous_app_in_focus() 89 | time.sleep(0.3) 90 | cmd_v() 91 | return self.processed_text 92 | 93 | def process_text(self, section: str, item: str) -> None: 94 | QTimer.singleShot(0, lambda: self.set_processed_text(section, item)) 95 | 96 | def on_press(self, key: keyboard.Key) -> None: 97 | try: 98 | # Detect if the pressed key is Cmd or Option (Alt) 99 | if key == keyboard.Key.alt_l or key == keyboard.Key.alt_r: 100 | self.option_pressed = True 101 | elif key == keyboard.Key.cmd_l or key == keyboard.Key.cmd_r: 102 | self.cmd_pressed = True 103 | 104 | # If both Cmd and Option keys are pressed simultaneously, open another window 105 | if self.cmd_pressed and self.option_pressed: 106 | if not self.text_window_open: 107 | self.open_text_window() 108 | self.text_window_open = True 109 | self.cmd_pressed = False # Reset after action to avoid repeated detection 110 | self.option_pressed = False 111 | 112 | # If only the Option key is pressed, follow the original logic 113 | elif self.option_pressed: 114 | current_time = time.time() 115 | 116 | # If two presses are detected within 1 second, open the window 117 | if current_time - self.last_time < 0.8: 118 | if self.floating_window_open: 119 | self.close_floating_window() 120 | else: 121 | self.open_floating_window() 122 | self.last_time = current_time 123 | self.option_pressed = False # Reset after action 124 | 125 | except AttributeError as e: 126 | LOG.debug(f"AttributeError: {e}") 127 | pass 128 | 129 | def _open_text_window(self) -> None: 130 | LOG.debug("Opening text window") 131 | self.recreate_text_window() 132 | 133 | def open_text_window(self) -> None: 134 | pass # This is still unreliable 135 | 136 | def open_floating_window(self) -> None: 137 | self.get_focused_text() 138 | self.return_app_in_focus() 139 | QTimer.singleShot(0, self.view.floating_window.show) 140 | self.floating_window_open = True 141 | 142 | def close_floating_window(self) -> None: 143 | QTimer.singleShot(0, self.view.floating_window.close) 144 | self.put_previous_app_in_focus() 145 | self.floating_window_open = False 146 | 147 | def on_release(self, key: keyboard.Key) -> None: 148 | # Reset flags when keys are released 149 | if key == keyboard.Key.alt_l or key == keyboard.Key.alt_r: 150 | self.option_pressed = False 151 | elif key == keyboard.Key.cmd_l or key == keyboard.Key.cmd_r: 152 | self.cmd_pressed = False 153 | -------------------------------------------------------------------------------- /core/model.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from utils import load_all_available_functions 4 | 5 | import extensions 6 | import extensions.ollama.example 7 | from extensions.variables import variables 8 | 9 | LOG = logging.getLogger(__name__) 10 | 11 | from functools import reduce 12 | 13 | 14 | class Model: 15 | def __init__(self): 16 | self.functions = load_all_available_functions(extensions) 17 | self.sections = self.get_sections() 18 | self.variables = variables 19 | 20 | def get_all_functions_flattened(self) -> dict[str, str]: 21 | return reduce(lambda x, y: {**x, **y}, self.functions.values(), {}) 22 | 23 | def get_sections(self) -> dict[str, list[str]]: 24 | sections = {section: list(functions.keys()) for section, functions in self.functions.items()} 25 | sections["variables"] = list(variables.keys()) 26 | 27 | return sections 28 | 29 | def process_text(self, section: str, function_name: str, input_text: str, **kwargs) -> str: 30 | if section == "variables": 31 | return self.variables[function_name] 32 | 33 | return self.functions[section][function_name](input_text, **kwargs) 34 | -------------------------------------------------------------------------------- /core/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/core/views/__init__.py -------------------------------------------------------------------------------- /core/views/floating_window.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from PyQt6.QtCore import Qt, pyqtSignal 4 | from PyQt6.QtGui import QCloseEvent, QHideEvent, QKeyEvent, QShowEvent 5 | from PyQt6.QtWidgets import QListWidget, QListWidgetItem, QTabWidget, QVBoxLayout, QWidget 6 | 7 | from core.views.styling.floating_window_style import FloatingWindowStyleOptions 8 | 9 | LOG = logging.getLogger(__name__) 10 | 11 | 12 | class FloatingWindow(QWidget): 13 | custom_signal = pyqtSignal(str, str) 14 | windows_event = pyqtSignal(bool) 15 | process_text_event = pyqtSignal(str, str, str) 16 | event_put_app_focus = pyqtSignal() 17 | 18 | def __init__(self, tab_sections: dict): 19 | super().__init__() 20 | # Set macOS-style window appearance 21 | self.setWindowTitle(FloatingWindowStyleOptions.title) 22 | self.setGeometry(200, 200, 400, 300) 23 | self.setStyleSheet(FloatingWindowStyleOptions.base) 24 | 25 | self.tab_widget = QTabWidget() 26 | self.tab_scroll_positions = {} 27 | self.set_up_tab_widget(tab_sections) 28 | 29 | def keyPressEvent(self, event: QKeyEvent) -> None: 30 | current_index = self.tab_widget.currentIndex() 31 | current_widget = self.tab_widget.currentWidget() 32 | 33 | if event.key() == Qt.Key.Key_Escape: 34 | self.close() 35 | 36 | # Retrieve the QListWidget inside the current tab 37 | if current_widget: 38 | list_widget = current_widget.findChild(QListWidget) 39 | if list_widget and isinstance(list_widget, QListWidget): 40 | # If the current tab contains a QListWidget 41 | if event.key() == Qt.Key.Key_Up: 42 | current_row = list_widget.currentRow() 43 | new_row = max(0, current_row - 1) 44 | list_widget.setCurrentRow(new_row) 45 | elif event.key() == Qt.Key.Key_Down: 46 | current_row = list_widget.currentRow() 47 | new_row = min(list_widget.count() - 1, current_row + 1) 48 | list_widget.setCurrentRow(new_row) 49 | 50 | if event.key() == Qt.Key.Key_Return: 51 | # Handle Enter key 52 | current_item = list_widget.currentItem() # Get the currently selected item 53 | if current_item: 54 | tab_name = self.tab_widget.tabText(current_index) 55 | row_text = current_item.text() 56 | self.handle_enter_key(tab_name, row_text, current_index) 57 | 58 | else: 59 | LOG.debug("No item selected in the current list.") 60 | else: 61 | LOG.debug("Current tab does not contain a QListWidget.") 62 | else: 63 | LOG.debug("No current widget in the tab.") 64 | 65 | # Handle Left and Right arrow keys to switch tabs 66 | if event.key() == Qt.Key.Key_Right: 67 | next_index = (current_index + 1) % self.tab_widget.count() 68 | self.tab_widget.setCurrentIndex(next_index) 69 | elif event.key() == Qt.Key.Key_Left: 70 | previous_index = (current_index - 1) % self.tab_widget.count() 71 | self.tab_widget.setCurrentIndex(previous_index) 72 | else: 73 | # Pass other key events to the parent class 74 | super().keyPressEvent(event) 75 | 76 | def set_up_tab_widget(self, tab_sections: dict) -> None: 77 | # Create the tab widget 78 | self.tab_widget.setTabsClosable(False) 79 | self.tab_widget.setStyleSheet(FloatingWindowStyleOptions.tab_widget) 80 | 81 | # Create tabs 82 | self.create_tabs(tab_sections) 83 | 84 | # Main layout 85 | main_layout = QVBoxLayout() 86 | main_layout.setContentsMargins(0, 0, 0, 0) 87 | main_layout.addWidget(self.tab_widget) 88 | self.setLayout(main_layout) 89 | 90 | # Initialize scroll positions for all tabs 91 | for i in range(self.tab_widget.count()): 92 | self.tab_scroll_positions[i] = 0 93 | 94 | def create_tabs(self, tab_sections: dict) -> None: 95 | for section_name, tab_section in tab_sections.items(): 96 | tab = QWidget() 97 | tab_layout = QVBoxLayout() 98 | 99 | list_widget = QListWidget() 100 | list_widget.setStyleSheet(FloatingWindowStyleOptions.list_widget) 101 | tab_layout.addWidget(list_widget) 102 | 103 | for key in tab_section: 104 | item = QListWidgetItem(key) 105 | list_widget.addItem(item) 106 | list_widget.setCurrentRow(0) # Set the current row to the first item 107 | 108 | tab.setLayout(tab_layout) # Set layout for the tab 109 | self.tab_widget.addTab(tab, section_name) # Add the tab 110 | 111 | def handle_enter_key(self, tab_name: str, text_item: str, tab_index: int) -> None: 112 | """Close the window and trigger the processText function.""" 113 | self.custom_signal.emit(tab_name, text_item) 114 | self.close() # Close the window 115 | 116 | def closeEvent(self, event: QCloseEvent) -> None: 117 | # Hide the window instead of closing it to prevent application shutdown 118 | event.ignore() 119 | self.hide() 120 | # Update the key listener state when the window is hidden 121 | self.windows_event.emit(False) 122 | 123 | def hideEvent(self, event: QHideEvent) -> None: 124 | # Ensure the key listener flag is updated when the window is hidden 125 | self.windows_event.emit(False) 126 | 127 | def showEvent(self, event: QShowEvent) -> None: 128 | super().showEvent(event) 129 | # Ensure the window is focused when shown 130 | self.raise_() # Raise the window to the top 131 | self.activateWindow() # Make the window active (focused) 132 | 133 | self.windows_event.emit(True) 134 | 135 | self.event_put_app_focus.emit() 136 | -------------------------------------------------------------------------------- /core/views/styling/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/core/views/styling/__init__.py -------------------------------------------------------------------------------- /core/views/styling/floating_window_style.py: -------------------------------------------------------------------------------- 1 | class FloatingWindowStyleOptions: 2 | geometry = "200, 200, 400, 300" 3 | 4 | title = "Orange GUI" 5 | 6 | base = """ 7 | QWidget { 8 | background-color: #f4f4f4; 9 | border: 1px solid #ccc; 10 | border-radius: 10px; 11 | padding: 0; 12 | } 13 | """ 14 | 15 | tab_widget = """ 16 | QTabWidget::pane { 17 | border: none; 18 | background: transparent; 19 | } 20 | QTabBar::tab { 21 | background: #f8f8f8; 22 | border: none; 23 | padding: 8px 16px; 24 | margin: 2px; 25 | border-top-left-radius: 8px; 26 | border-top-right-radius: 8px; 27 | } 28 | QTabBar::tab:selected { 29 | background: #ffffff; 30 | font-weight: bold; 31 | color: #007aff; 32 | } 33 | QTabBar::tab:hover { 34 | background: #e9e9e9; 35 | } 36 | """ 37 | 38 | search_bar = """ 39 | QLineEdit { 40 | background-color: #ffffff; 41 | border: 1px solid #d1d1d1; 42 | border-radius: 6px; 43 | padding: 6px; 44 | margin: 6px 0; 45 | } 46 | """ 47 | 48 | list_widget = """ 49 | QListWidget { 50 | background-color: #ffffff; 51 | border: 1px solid #d1d1d1; 52 | border-radius: 6px; 53 | margin: 8px; 54 | } 55 | QListWidget::item { 56 | padding: 8px; 57 | font-size: 14px; 58 | color: #333; 59 | } 60 | QListWidget::item:selected { 61 | background-color: #007aff; 62 | color: #ffffff; 63 | font-weight: bold; 64 | border-radius: 4px; 65 | margin: 2px; 66 | } 67 | QListWidget::item:hover { 68 | background-color: #e0e0e0; 69 | color: #000; 70 | } 71 | """ 72 | -------------------------------------------------------------------------------- /core/views/system_tray.py: -------------------------------------------------------------------------------- 1 | from config import CONFIG 2 | from PyQt6.QtCore import QCoreApplication 3 | from PyQt6.QtGui import QAction, QIcon 4 | from PyQt6.QtWidgets import QMenu, QSystemTrayIcon 5 | 6 | 7 | class SystemTray(QSystemTrayIcon): 8 | def __init__(self): 9 | icon = CONFIG.get("app").get("icon") 10 | 11 | super().__init__(QIcon(icon)) 12 | 13 | # Create the tray menu 14 | self.menu = QMenu() 15 | 16 | # Add actions to the menu 17 | self.create_menu_actions() 18 | 19 | # Set the menu to the tray icon 20 | self.setContextMenu(self.menu) 21 | 22 | def create_menu_actions(self): 23 | """Creates and adds actions to the context menu.""" 24 | 25 | # Action to quit the application 26 | quit_action = QAction("❌ Quit", self) 27 | quit_action.triggered.connect(self.quit_app) 28 | quit_action.setToolTip("Quit the application") 29 | 30 | # Add actions to the menu 31 | self.menu.addAction(quit_action) 32 | 33 | def open_settings(self): 34 | """Placeholder for a settings dialog.""" 35 | self.show_message("Settings", "Settings window would appear here.") 36 | 37 | def show_message(self, title: str, message: str): 38 | """Displays a message balloon from the system tray.""" 39 | self.showMessage(title, message, QSystemTrayIcon.MessageIcon.Information) 40 | 41 | def quit_app(self): 42 | """Exits the application.""" 43 | QCoreApplication.quit() 44 | -------------------------------------------------------------------------------- /core/views/text_processing.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from PyQt6.QtCore import Qt, pyqtSignal 4 | from PyQt6.QtGui import QCloseEvent, QHideEvent, QShowEvent 5 | from PyQt6.QtWidgets import ( 6 | QAbstractItemView, 7 | QFrame, 8 | QHBoxLayout, 9 | QLineEdit, 10 | QListWidget, 11 | QPushButton, 12 | QSplitter, 13 | QTabWidget, 14 | QTextEdit, 15 | QVBoxLayout, 16 | QWidget, 17 | ) 18 | 19 | LOG = logging.getLogger(__name__) 20 | 21 | 22 | class TextWindow(QWidget): 23 | custom_signal = pyqtSignal(str, str) 24 | windows_event = pyqtSignal(bool) 25 | process_text_event = pyqtSignal(str, str, str) 26 | event_put_app_focus = pyqtSignal() 27 | 28 | def __init__(self, processing_text, functions_list): 29 | super().__init__() 30 | LOG.debug(f"Creating text window processing text, {processing_text}") 31 | # Layout for the main window 32 | main_layout = QHBoxLayout(self) 33 | 34 | self.processing_text = processing_text 35 | 36 | # Create QTabWidget for text areas 37 | self.text_tab_widget = QTabWidget() 38 | self.functions_list = functions_list 39 | # Add initial text tab 40 | self.text_tab = QWidget() 41 | text_layout = QVBoxLayout(self.text_tab) 42 | self.text_widget = QTextEdit() 43 | self.text_widget.setText(self.processing_text) 44 | text_layout.addWidget(self.text_widget) 45 | self.text_tab_widget.addTab(self.text_tab, "Text 1") 46 | 47 | # Create QTabWidget for functions list 48 | self.function_tab_widget = QTabWidget() 49 | 50 | # Tab 1 - Function list 51 | self.function_list_tab = QWidget() 52 | function_layout = QVBoxLayout(self.function_list_tab) 53 | self.function_list_widget = QListWidget() 54 | function_layout.addWidget(self.function_list_widget) 55 | self.function_tab_widget.addTab(self.function_list_tab, "Functions 1") 56 | 57 | # Add function names to the list 58 | for function_name in functions_list: 59 | self.function_list_widget.addItem(function_name) 60 | 61 | # Set the function list widget to be selectable 62 | self.function_list_widget.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) 63 | 64 | # Tab 2 - Text area for functions 65 | self.function_text_tab = QWidget() 66 | function_text_layout = QVBoxLayout(self.function_text_tab) 67 | function_text_widget = QTextEdit() 68 | function_text_layout.addWidget(function_text_widget) 69 | self.function_tab_widget.addTab(self.function_text_tab, "Function Text") 70 | 71 | # Split the main layout into two sections: one for text and one for functions 72 | splitter = QSplitter(Qt.Orientation.Horizontal) 73 | splitter.addWidget(self.text_tab_widget) 74 | splitter.addWidget(self.function_tab_widget) 75 | 76 | # Set stretch factors to ensure text and function sections take 90% of the space 77 | splitter.setStretchFactor(0, 9) # Text section takes 9 parts 78 | splitter.setStretchFactor(1, 9) # Function section takes 9 parts 79 | 80 | # Add splitter to the main layout 81 | main_layout.addWidget(splitter) 82 | 83 | # Bottom input area with 3 text inputs and buttons, occupying 10% of the horizontal space 84 | bottom_layout = QVBoxLayout() 85 | 86 | # Create a container frame for the bottom section to control its width 87 | bottom_frame = QFrame() 88 | bottom_frame.setLayout(bottom_layout) 89 | 90 | # Create 3 text input fields and 3 run buttons, and add them vertically 91 | self.inputs = [QLineEdit() for _ in range(3)] 92 | self.run_buttons = [QPushButton("Run") for _ in range(3)] 93 | 94 | for input_field, button in zip(self.inputs, self.run_buttons): 95 | bottom_layout.addWidget(input_field) 96 | bottom_layout.addWidget(button) 97 | 98 | # Add the bottom frame to the main layout, occupying 10% of the horizontal space 99 | bottom_frame.setFixedWidth(self.width() // 10) # Set the width to 10% of the window size 100 | main_layout.addWidget(bottom_frame) 101 | 102 | self.setLayout(main_layout) 103 | self.setWindowTitle("Text Display and Function List") 104 | self.resize(800, 600) 105 | 106 | # Connect context menus for adding new tabs 107 | self.text_tab_widget.tabBar().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 108 | self.text_tab_widget.tabBar().customContextMenuRequested.connect(self.showTextTabMenu) 109 | 110 | self.function_tab_widget.tabBar().setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 111 | self.function_tab_widget.tabBar().customContextMenuRequested.connect(self.showFunctionTabMenu) 112 | 113 | # Connect the Enter key event to the function application 114 | self.function_list_widget.keyPressEvent = self.handle_function_key_event 115 | 116 | def handle_function_key_event(self, event): 117 | if event.key() == Qt.Key.Key_Return: # Detect Enter key press 118 | # Get the selected function name from the list 119 | selected_item = self.function_list_widget.currentItem() 120 | if selected_item: 121 | function_name = selected_item.text() 122 | # Apply the function to the displayed text 123 | self.apply_function(function_name) 124 | else: 125 | super().keyPressEvent(event) # Pass the event to the base class if it's not Enter 126 | 127 | def apply_function(self, function_name): 128 | # Check if the function name exists in the functions list 129 | 130 | if function_name in self.functions_list: 131 | # Apply the function to the current text in the text widget 132 | func = self.functions_list[function_name] 133 | new_text = func(self.text_widget.toPlainText()) 134 | self.text_widget.setText(new_text) # Update the text widget with the result 135 | 136 | def showTextTabMenu(self, pos): 137 | # Create and show context menu for adding new text tabs 138 | menu = self.text_tab_widget.tabBar().createStandardContextMenu() 139 | add_tab_action = menu.addAction("Add New Text Tab") 140 | add_tab_action.triggered.connect(self.add_new_text_tab) 141 | menu.exec(self.text_tab_widget.tabBar().mapToGlobal(pos)) 142 | 143 | def showFunctionTabMenu(self, pos): 144 | # Create and show context menu for adding new function tabs 145 | menu = self.function_tab_widget.tabBar().createStandardContextMenu() 146 | add_tab_action = menu.addAction("Add New Function Tab") 147 | add_tab_action.triggered.connect(self.add_new_function_tab) 148 | menu.exec(self.function_tab_widget.tabBar().mapToGlobal(pos)) 149 | 150 | def add_new_text_tab(self): 151 | # Create new text tab 152 | new_text_tab = QWidget() 153 | new_text_layout = QVBoxLayout(new_text_tab) 154 | new_text_widget = QTextEdit() 155 | new_text_layout.addWidget(new_text_widget) 156 | 157 | # Add the new text tab 158 | new_tab_index = self.text_tab_widget.addTab(new_text_tab, f"Text {self.text_tab_widget.count() + 1}") 159 | 160 | # Optionally set the new tab as the current tab 161 | self.text_tab_widget.setCurrentIndex(new_tab_index) 162 | 163 | def add_new_function_tab(self): 164 | # Create new function list tab 165 | new_function_list_tab = QWidget() 166 | new_function_list_layout = QVBoxLayout(new_function_list_tab) 167 | new_function_list_widget = QListWidget() 168 | new_function_list_widget.addItem("Uppercase") # Default function item 169 | new_function_list_widget.addItem("Lowercase") 170 | new_function_list_layout.addWidget(new_function_list_widget) 171 | 172 | # Add the new function list tab 173 | new_function_list_tab_index = self.function_tab_widget.addTab( 174 | new_function_list_tab, f"Functions {self.function_tab_widget.count() + 1}" 175 | ) 176 | 177 | # Create new function text tab 178 | new_function_text_tab = QWidget() 179 | new_function_text_layout = QVBoxLayout(new_function_text_tab) 180 | new_function_text_widget = QTextEdit() 181 | new_function_text_layout.addWidget(new_function_text_widget) 182 | 183 | # Add the new function text tab 184 | new_function_text_tab_index = self.function_tab_widget.addTab( 185 | new_function_text_tab, f"Function Text {self.function_tab_widget.count()}" 186 | ) 187 | 188 | # Optionally set the new function list tab as the current tab 189 | self.function_tab_widget.setCurrentIndex(new_function_list_tab_index) 190 | 191 | def closeEvent(self, event: QCloseEvent) -> None: 192 | # Hide the window instead of closing it to prevent application shutdown 193 | event.ignore() 194 | self.hide() 195 | # Update the key listener state when the window is hidden 196 | self.windows_event.emit(False) 197 | 198 | def hideEvent(self, event: QHideEvent) -> None: 199 | # Ensure the key listener flag is updated when the window is hidden 200 | self.windows_event.emit(False) 201 | 202 | def showEvent(self, event: QShowEvent) -> None: 203 | LOG.debug("Text window shown") 204 | super().showEvent(event) 205 | # Ensure the window is focused when shown 206 | self.raise_() # Raise the window to the top 207 | self.activateWindow() # Make the window active (focused) 208 | 209 | self.windows_event.emit(True) 210 | 211 | self.event_put_app_focus.emit() 212 | -------------------------------------------------------------------------------- /example1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/example1.gif -------------------------------------------------------------------------------- /example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/example2.gif -------------------------------------------------------------------------------- /extensions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/extensions/__init__.py -------------------------------------------------------------------------------- /extensions/basics/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def upper_case(text: str, **kwargs) -> str: 5 | return text.upper() 6 | 7 | 8 | def lower_case(text: str, **kwargs) -> str: 9 | return text.lower() 10 | 11 | 12 | def pretty_json(text: str, **kwargs) -> str: 13 | return json.dumps(json.loads(text), indent=4) 14 | 15 | 16 | def a_complex_task_you_do_not_want_to_implement_now(text: str, **kwargs) -> str: 17 | return "This is a complex task that you do not want to implement now." 18 | -------------------------------------------------------------------------------- /extensions/langraph/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/extensions/langraph/__init__.py -------------------------------------------------------------------------------- /extensions/langraph/langraph.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/extensions/langraph/langraph.py -------------------------------------------------------------------------------- /extensions/ollama/__init__.py: -------------------------------------------------------------------------------- 1 | from extensions.ollama.example import improve_grammar, make_it_polite, translate_to_english # noqa: F401 2 | -------------------------------------------------------------------------------- /extensions/ollama/example.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from config import CONFIG 4 | 5 | from ollama import ChatResponse, chat 6 | 7 | LOG = logging.getLogger(__name__) 8 | 9 | 10 | def ollama(prompt: str) -> str: 11 | response: ChatResponse = chat( 12 | model=CONFIG["ollama"]["default_model"], 13 | messages=[ 14 | { 15 | "role": "user", 16 | "content": prompt, 17 | }, 18 | ], 19 | ) 20 | # or access fields directly from the response object 21 | LOG.debug(response.message.content) 22 | return response.message.content 23 | 24 | 25 | def improve_grammar(input_text: str, **kwargs) -> str: 26 | prompt = ( 27 | "Improve the grammar of this sentence. Return only the improved sentence, do not add anything else. " 28 | + input_text 29 | ) 30 | return ollama(prompt) 31 | 32 | 33 | def translate_to_english(input_text: str, **kwargs) -> str: 34 | prompt = ( 35 | "Translate this sentence to English. Return only the translated sentence, do not add anything else. " 36 | + input_text 37 | ) 38 | return ollama(prompt) 39 | 40 | 41 | def make_it_polite(input_text: str, **kwargs) -> str: 42 | prompt = ( 43 | "Convert this sentence to a polite one. Return only the converted sentence, do not add anything else. " 44 | + input_text 45 | ) 46 | return ollama(prompt) 47 | -------------------------------------------------------------------------------- /extensions/openai/__init__.py: -------------------------------------------------------------------------------- 1 | from extensions.openai.utils import _chat_completion_endpoint 2 | 3 | 4 | def make_a_joke(text: str, **kwargs) -> str: 5 | prompt = f"Tell me a joke about {text}" 6 | 7 | return _chat_completion_endpoint(prompt) 8 | -------------------------------------------------------------------------------- /extensions/openai/utils.py: -------------------------------------------------------------------------------- 1 | from config import CONFIG 2 | 3 | from openai import OpenAI 4 | 5 | 6 | def _chat_completion_endpoint(content: str) -> str: 7 | client = OpenAI(api_key=CONFIG["openai"]["api_key"]) 8 | 9 | chat_completion = client.chat.completions.create( 10 | messages=[ 11 | { 12 | "role": "user", 13 | "content": content, 14 | } 15 | ], 16 | model=CONFIG["openai"]["default_model"], 17 | ) 18 | 19 | return chat_completion.choices[0].message.content 20 | -------------------------------------------------------------------------------- /extensions/variables.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | dev_db_user = os.environ.get("dev_db_user") or "user" 4 | 5 | variables = { 6 | "my name:": "Yoda", 7 | "thing i always forget": "42", 8 | "s3 bucket with a weird name": "prod-thing-i-always-forget", 9 | "Prd kafka endpoint": "https://prod-thing-i-always-forget.s3.amazonaws.com", 10 | "that thing once told me": "John Snow knows nothing", 11 | "Json config dev": '{"s3bucket":"dev-bucket", "db": {"user": "%s"} }' % dev_db_user, 12 | } 13 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | PROJECT ?= orange-intelligence 2 | VERSION ?= $(shell grep -m 1 version pyproject.toml | tr -s ' ' | tr -d '"' | tr -d "'" | cut -d' ' -f3) 3 | PYTHON_VERSION = $(shell grep 'python =' pyproject.toml | sed -n 's/^python = ["^]*\([0-9]*\.[0-9]*\)\(.*\)"/\1/p') 4 | 5 | version: ## Show the current version 6 | echo "Current version: $(VERSION)" 7 | 8 | help: ## Show help 9 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort |\ 10 | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-16s\033[0m %s\n", $$1, $$2}' 11 | 12 | .DEFAULT_GOAL=help 13 | 14 | .python-version: ## Installs the correct version of python if required 15 | $(if $(shell pyenv versions --bare | grep "^$(PYTHON_VERSION)"),, pyenv install $(PYTHON_VERSION)) 16 | pyenv local $(shell pyenv versions --bare | grep "^$(PYTHON_VERSION)" | tail -n 1) 17 | 18 | .venv: .python-version ## Activates and installs the poetry environment. 19 | poetry env use ~/.pyenv/versions/$(shell cat $<)/bin/python && \ 20 | poetry install 21 | 22 | lint: .venv ## Lint the project 23 | poetry run flake8 24 | 25 | mypy: .venv ## Run mypy 26 | poetry run mypy 27 | 28 | test: .venv ## Test the project 29 | poetry run pytest 30 | 31 | fmt: .venv ## Run formatting tools for project 32 | poetry run black . $(if $(CI),--check ,) && poetry run isort . $(if $(CI),--check ,) 33 | 34 | bandit: .venv ## Run bandit 35 | poetry run bandit -c pyproject.toml -r . 36 | 37 | check: fmt lint mypy bandit test ; ## Run all checks 38 | 39 | run: 40 | poetry run python3 app.py 41 | 42 | clean: ## Clean the project 43 | rm -rf .venv 44 | rm -rf .python-version 45 | rm -rf dist/ 46 | rm -rf __pycache__ 47 | rm -rf .pytest_cache 48 | rm -rf .mypy_cache 49 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "annotated-types" 5 | version = "0.7.0" 6 | description = "Reusable constraint types to use with typing.Annotated" 7 | optional = false 8 | python-versions = ">=3.8" 9 | groups = ["main"] 10 | files = [ 11 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 12 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 13 | ] 14 | 15 | [[package]] 16 | name = "anyio" 17 | version = "4.8.0" 18 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 19 | optional = false 20 | python-versions = ">=3.9" 21 | groups = ["main"] 22 | files = [ 23 | {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, 24 | {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, 25 | ] 26 | 27 | [package.dependencies] 28 | exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} 29 | idna = ">=2.8" 30 | sniffio = ">=1.1" 31 | typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} 32 | 33 | [package.extras] 34 | doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] 35 | test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] 36 | trio = ["trio (>=0.26.1)"] 37 | 38 | [[package]] 39 | name = "astor" 40 | version = "0.8.1" 41 | description = "Read/rewrite/write Python ASTs" 42 | optional = false 43 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 44 | groups = ["dev"] 45 | files = [ 46 | {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, 47 | {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, 48 | ] 49 | 50 | [[package]] 51 | name = "attrs" 52 | version = "25.1.0" 53 | description = "Classes Without Boilerplate" 54 | optional = false 55 | python-versions = ">=3.8" 56 | groups = ["dev"] 57 | files = [ 58 | {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, 59 | {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, 60 | ] 61 | 62 | [package.extras] 63 | benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] 64 | cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] 65 | dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] 66 | docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] 67 | tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] 68 | tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] 69 | 70 | [[package]] 71 | name = "bandit" 72 | version = "1.8.2" 73 | description = "Security oriented static analyser for python code." 74 | optional = false 75 | python-versions = ">=3.9" 76 | groups = ["dev"] 77 | files = [ 78 | {file = "bandit-1.8.2-py3-none-any.whl", hash = "sha256:df6146ad73dd30e8cbda4e29689ddda48364e36ff655dbfc86998401fcf1721f"}, 79 | {file = "bandit-1.8.2.tar.gz", hash = "sha256:e00ad5a6bc676c0954669fe13818024d66b70e42cf5adb971480cf3b671e835f"}, 80 | ] 81 | 82 | [package.dependencies] 83 | colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} 84 | PyYAML = ">=5.3.1" 85 | rich = "*" 86 | stevedore = ">=1.20.0" 87 | tomli = {version = ">=1.1.0", optional = true, markers = "python_version < \"3.11\" and extra == \"toml\""} 88 | 89 | [package.extras] 90 | baseline = ["GitPython (>=3.1.30)"] 91 | sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] 92 | test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] 93 | toml = ["tomli (>=1.1.0)"] 94 | yaml = ["PyYAML"] 95 | 96 | [[package]] 97 | name = "black" 98 | version = "23.12.1" 99 | description = "The uncompromising code formatter." 100 | optional = false 101 | python-versions = ">=3.8" 102 | groups = ["dev"] 103 | files = [ 104 | {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, 105 | {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, 106 | {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, 107 | {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, 108 | {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, 109 | {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, 110 | {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, 111 | {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, 112 | {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, 113 | {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, 114 | {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, 115 | {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, 116 | {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, 117 | {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, 118 | {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, 119 | {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, 120 | {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, 121 | {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, 122 | {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, 123 | {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, 124 | {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, 125 | {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, 126 | ] 127 | 128 | [package.dependencies] 129 | click = ">=8.0.0" 130 | mypy-extensions = ">=0.4.3" 131 | packaging = ">=22.0" 132 | pathspec = ">=0.9.0" 133 | platformdirs = ">=2" 134 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 135 | typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} 136 | 137 | [package.extras] 138 | colorama = ["colorama (>=0.4.3)"] 139 | d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] 140 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 141 | uvloop = ["uvloop (>=0.15.2)"] 142 | 143 | [[package]] 144 | name = "certifi" 145 | version = "2024.12.14" 146 | description = "Python package for providing Mozilla's CA Bundle." 147 | optional = false 148 | python-versions = ">=3.6" 149 | groups = ["main"] 150 | files = [ 151 | {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, 152 | {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, 153 | ] 154 | 155 | [[package]] 156 | name = "click" 157 | version = "8.1.8" 158 | description = "Composable command line interface toolkit" 159 | optional = false 160 | python-versions = ">=3.7" 161 | groups = ["dev"] 162 | files = [ 163 | {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, 164 | {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, 165 | ] 166 | 167 | [package.dependencies] 168 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 169 | 170 | [[package]] 171 | name = "colorama" 172 | version = "0.4.6" 173 | description = "Cross-platform colored terminal text." 174 | optional = false 175 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 176 | groups = ["main", "dev"] 177 | files = [ 178 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 179 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 180 | ] 181 | markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} 182 | 183 | [[package]] 184 | name = "coverage" 185 | version = "7.6.10" 186 | description = "Code coverage measurement for Python" 187 | optional = false 188 | python-versions = ">=3.9" 189 | groups = ["dev"] 190 | files = [ 191 | {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, 192 | {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, 193 | {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, 194 | {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, 195 | {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, 196 | {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, 197 | {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, 198 | {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, 199 | {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, 200 | {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, 201 | {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, 202 | {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, 203 | {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, 204 | {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, 205 | {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, 206 | {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, 207 | {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, 208 | {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, 209 | {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, 210 | {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, 211 | {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, 212 | {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, 213 | {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, 214 | {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, 215 | {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, 216 | {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, 217 | {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, 218 | {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, 219 | {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, 220 | {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, 221 | {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, 222 | {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, 223 | {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, 224 | {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, 225 | {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, 226 | {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, 227 | {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, 228 | {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, 229 | {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, 230 | {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, 231 | {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, 232 | {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, 233 | {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, 234 | {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, 235 | {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, 236 | {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, 237 | {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, 238 | {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, 239 | {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, 240 | {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, 241 | {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, 242 | {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, 243 | {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, 244 | {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, 245 | {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, 246 | {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, 247 | {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, 248 | {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, 249 | {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, 250 | {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, 251 | {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, 252 | {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, 253 | ] 254 | 255 | [package.dependencies] 256 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 257 | 258 | [package.extras] 259 | toml = ["tomli"] 260 | 261 | [[package]] 262 | name = "distro" 263 | version = "1.9.0" 264 | description = "Distro - an OS platform information API" 265 | optional = false 266 | python-versions = ">=3.6" 267 | groups = ["main"] 268 | files = [ 269 | {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, 270 | {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, 271 | ] 272 | 273 | [[package]] 274 | name = "eradicate" 275 | version = "2.3.0" 276 | description = "Removes commented-out code." 277 | optional = false 278 | python-versions = "*" 279 | groups = ["dev"] 280 | files = [ 281 | {file = "eradicate-2.3.0-py3-none-any.whl", hash = "sha256:2b29b3dd27171f209e4ddd8204b70c02f0682ae95eecb353f10e8d72b149c63e"}, 282 | {file = "eradicate-2.3.0.tar.gz", hash = "sha256:06df115be3b87d0fc1c483db22a2ebb12bcf40585722810d809cc770f5031c37"}, 283 | ] 284 | 285 | [[package]] 286 | name = "evdev" 287 | version = "1.8.0" 288 | description = "Bindings to the Linux input handling subsystem" 289 | optional = false 290 | python-versions = ">=3.8" 291 | groups = ["main"] 292 | markers = "sys_platform in \"linux\"" 293 | files = [ 294 | {file = "evdev-1.8.0.tar.gz", hash = "sha256:45598eee1ae3876a3122ca1dc0ec8049c01931672d12478b5c610afc24e47d75"}, 295 | ] 296 | 297 | [[package]] 298 | name = "exceptiongroup" 299 | version = "1.2.2" 300 | description = "Backport of PEP 654 (exception groups)" 301 | optional = false 302 | python-versions = ">=3.7" 303 | groups = ["main", "dev"] 304 | markers = "python_version < \"3.11\"" 305 | files = [ 306 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 307 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 308 | ] 309 | 310 | [package.extras] 311 | test = ["pytest (>=6)"] 312 | 313 | [[package]] 314 | name = "flake8" 315 | version = "6.1.0" 316 | description = "the modular source code checker: pep8 pyflakes and co" 317 | optional = false 318 | python-versions = ">=3.8.1" 319 | groups = ["dev"] 320 | files = [ 321 | {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, 322 | {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, 323 | ] 324 | 325 | [package.dependencies] 326 | mccabe = ">=0.7.0,<0.8.0" 327 | pycodestyle = ">=2.11.0,<2.12.0" 328 | pyflakes = ">=3.1.0,<3.2.0" 329 | 330 | [[package]] 331 | name = "flake8-annotations" 332 | version = "3.1.1" 333 | description = "Flake8 Type Annotation Checks" 334 | optional = false 335 | python-versions = ">=3.8.1" 336 | groups = ["dev"] 337 | files = [ 338 | {file = "flake8_annotations-3.1.1-py3-none-any.whl", hash = "sha256:102935bdcbfa714759a152aeb07b14aee343fc0b6f7c55ad16968ce3e0e91a8a"}, 339 | {file = "flake8_annotations-3.1.1.tar.gz", hash = "sha256:6c98968ccc6bdc0581d363bf147a87df2f01d0d078264b2da805799d911cf5fe"}, 340 | ] 341 | 342 | [package.dependencies] 343 | attrs = ">=21.4" 344 | flake8 = ">=5.0" 345 | 346 | [[package]] 347 | name = "flake8-bugbear" 348 | version = "24.12.12" 349 | description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." 350 | optional = false 351 | python-versions = ">=3.8.1" 352 | groups = ["dev"] 353 | files = [ 354 | {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, 355 | {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, 356 | ] 357 | 358 | [package.dependencies] 359 | attrs = ">=22.2.0" 360 | flake8 = ">=6.0.0" 361 | 362 | [package.extras] 363 | dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] 364 | 365 | [[package]] 366 | name = "flake8-eradicate" 367 | version = "1.5.0" 368 | description = "Flake8 plugin to find commented out code" 369 | optional = false 370 | python-versions = ">=3.8,<4.0" 371 | groups = ["dev"] 372 | files = [ 373 | {file = "flake8_eradicate-1.5.0-py3-none-any.whl", hash = "sha256:18acc922ad7de623f5247c7d5595da068525ec5437dd53b22ec2259b96ce9d22"}, 374 | {file = "flake8_eradicate-1.5.0.tar.gz", hash = "sha256:aee636cb9ecb5594a7cd92d67ad73eb69909e5cc7bd81710cf9d00970f3983a6"}, 375 | ] 376 | 377 | [package.dependencies] 378 | attrs = "*" 379 | eradicate = ">=2.0,<3.0" 380 | flake8 = ">5" 381 | 382 | [[package]] 383 | name = "flake8-plugin-utils" 384 | version = "1.3.3" 385 | description = "The package provides base classes and utils for flake8 plugin writing" 386 | optional = false 387 | python-versions = ">=3.6,<4.0" 388 | groups = ["dev"] 389 | files = [ 390 | {file = "flake8-plugin-utils-1.3.3.tar.gz", hash = "sha256:39f6f338d038b301c6fd344b06f2e81e382b68fa03c0560dff0d9b1791a11a2c"}, 391 | {file = "flake8_plugin_utils-1.3.3-py3-none-any.whl", hash = "sha256:e4848c57d9d50f19100c2d75fa794b72df068666a9041b4b0409be923356a3ed"}, 392 | ] 393 | 394 | [[package]] 395 | name = "flake8-print" 396 | version = "5.0.0" 397 | description = "print statement checker plugin for flake8" 398 | optional = false 399 | python-versions = ">=3.7" 400 | groups = ["dev"] 401 | files = [ 402 | {file = "flake8-print-5.0.0.tar.gz", hash = "sha256:76915a2a389cc1c0879636c219eb909c38501d3a43cc8dae542081c9ba48bdf9"}, 403 | {file = "flake8_print-5.0.0-py3-none-any.whl", hash = "sha256:84a1a6ea10d7056b804221ac5e62b1cee1aefc897ce16f2e5c42d3046068f5d8"}, 404 | ] 405 | 406 | [package.dependencies] 407 | flake8 = ">=3.0" 408 | pycodestyle = "*" 409 | 410 | [[package]] 411 | name = "flake8-pytest-style" 412 | version = "1.7.2" 413 | description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." 414 | optional = false 415 | python-versions = ">=3.7.2,<4.0.0" 416 | groups = ["dev"] 417 | files = [ 418 | {file = "flake8_pytest_style-1.7.2-py3-none-any.whl", hash = "sha256:f5d2aa3219163a052dd92226589d45fab8ea027a3269922f0c4029f548ea5cd1"}, 419 | {file = "flake8_pytest_style-1.7.2.tar.gz", hash = "sha256:b924197c99b951315949920b0e5547f34900b1844348432e67a44ab191582109"}, 420 | ] 421 | 422 | [package.dependencies] 423 | flake8-plugin-utils = ">=1.3.2,<2.0.0" 424 | 425 | [[package]] 426 | name = "flake8-simplify" 427 | version = "0.21.0" 428 | description = "flake8 plugin which checks for code that can be simplified" 429 | optional = false 430 | python-versions = ">=3.6.1" 431 | groups = ["dev"] 432 | files = [ 433 | {file = "flake8_simplify-0.21.0-py3-none-any.whl", hash = "sha256:439391e762a9370b371208add0b5c5c40c3d25a98e1f5421d263215d08194183"}, 434 | {file = "flake8_simplify-0.21.0.tar.gz", hash = "sha256:c95ff1dcc1de5949af47e0087cbf1164445881131b15bcd7a71252670f492f4d"}, 435 | ] 436 | 437 | [package.dependencies] 438 | astor = ">=0.1" 439 | flake8 = ">=3.7" 440 | 441 | [[package]] 442 | name = "h11" 443 | version = "0.14.0" 444 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 445 | optional = false 446 | python-versions = ">=3.7" 447 | groups = ["main"] 448 | files = [ 449 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 450 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 451 | ] 452 | 453 | [[package]] 454 | name = "httpcore" 455 | version = "1.0.7" 456 | description = "A minimal low-level HTTP client." 457 | optional = false 458 | python-versions = ">=3.8" 459 | groups = ["main"] 460 | files = [ 461 | {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, 462 | {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, 463 | ] 464 | 465 | [package.dependencies] 466 | certifi = "*" 467 | h11 = ">=0.13,<0.15" 468 | 469 | [package.extras] 470 | asyncio = ["anyio (>=4.0,<5.0)"] 471 | http2 = ["h2 (>=3,<5)"] 472 | socks = ["socksio (==1.*)"] 473 | trio = ["trio (>=0.22.0,<1.0)"] 474 | 475 | [[package]] 476 | name = "httpx" 477 | version = "0.28.1" 478 | description = "The next generation HTTP client." 479 | optional = false 480 | python-versions = ">=3.8" 481 | groups = ["main"] 482 | files = [ 483 | {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, 484 | {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, 485 | ] 486 | 487 | [package.dependencies] 488 | anyio = "*" 489 | certifi = "*" 490 | httpcore = "==1.*" 491 | idna = "*" 492 | 493 | [package.extras] 494 | brotli = ["brotli", "brotlicffi"] 495 | cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] 496 | http2 = ["h2 (>=3,<5)"] 497 | socks = ["socksio (==1.*)"] 498 | zstd = ["zstandard (>=0.18.0)"] 499 | 500 | [[package]] 501 | name = "idna" 502 | version = "3.10" 503 | description = "Internationalized Domain Names in Applications (IDNA)" 504 | optional = false 505 | python-versions = ">=3.6" 506 | groups = ["main"] 507 | files = [ 508 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 509 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 510 | ] 511 | 512 | [package.extras] 513 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 514 | 515 | [[package]] 516 | name = "iniconfig" 517 | version = "2.0.0" 518 | description = "brain-dead simple config-ini parsing" 519 | optional = false 520 | python-versions = ">=3.7" 521 | groups = ["dev"] 522 | files = [ 523 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 524 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 525 | ] 526 | 527 | [[package]] 528 | name = "isort" 529 | version = "5.13.2" 530 | description = "A Python utility / library to sort Python imports." 531 | optional = false 532 | python-versions = ">=3.8.0" 533 | groups = ["dev"] 534 | files = [ 535 | {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, 536 | {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, 537 | ] 538 | 539 | [package.extras] 540 | colors = ["colorama (>=0.4.6)"] 541 | 542 | [[package]] 543 | name = "jiter" 544 | version = "0.8.2" 545 | description = "Fast iterable JSON parser." 546 | optional = false 547 | python-versions = ">=3.8" 548 | groups = ["main"] 549 | files = [ 550 | {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, 551 | {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, 552 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d"}, 553 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66"}, 554 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5"}, 555 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3"}, 556 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08"}, 557 | {file = "jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49"}, 558 | {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d"}, 559 | {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff"}, 560 | {file = "jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43"}, 561 | {file = "jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105"}, 562 | {file = "jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b"}, 563 | {file = "jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15"}, 564 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0"}, 565 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f"}, 566 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099"}, 567 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74"}, 568 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586"}, 569 | {file = "jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc"}, 570 | {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88"}, 571 | {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6"}, 572 | {file = "jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44"}, 573 | {file = "jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855"}, 574 | {file = "jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f"}, 575 | {file = "jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44"}, 576 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f"}, 577 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60"}, 578 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57"}, 579 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e"}, 580 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887"}, 581 | {file = "jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d"}, 582 | {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152"}, 583 | {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29"}, 584 | {file = "jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e"}, 585 | {file = "jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c"}, 586 | {file = "jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84"}, 587 | {file = "jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4"}, 588 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587"}, 589 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c"}, 590 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18"}, 591 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6"}, 592 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef"}, 593 | {file = "jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1"}, 594 | {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9"}, 595 | {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05"}, 596 | {file = "jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a"}, 597 | {file = "jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865"}, 598 | {file = "jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca"}, 599 | {file = "jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0"}, 600 | {file = "jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566"}, 601 | {file = "jiter-0.8.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9e1fa156ee9454642adb7e7234a383884452532bc9d53d5af2d18d98ada1d79c"}, 602 | {file = "jiter-0.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cf5dfa9956d96ff2efb0f8e9c7d055904012c952539a774305aaaf3abdf3d6c"}, 603 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e52bf98c7e727dd44f7c4acb980cb988448faeafed8433c867888268899b298b"}, 604 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a2ecaa3c23e7a7cf86d00eda3390c232f4d533cd9ddea4b04f5d0644faf642c5"}, 605 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08d4c92bf480e19fc3f2717c9ce2aa31dceaa9163839a311424b6862252c943e"}, 606 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d9a1eded738299ba8e106c6779ce5c3893cffa0e32e4485d680588adae6db8"}, 607 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20be8b7f606df096e08b0b1b4a3c6f0515e8dac296881fe7461dfa0fb5ec817"}, 608 | {file = "jiter-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d33f94615fcaf872f7fd8cd98ac3b429e435c77619777e8a449d9d27e01134d1"}, 609 | {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:317b25e98a35ffec5c67efe56a4e9970852632c810d35b34ecdd70cc0e47b3b6"}, 610 | {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc9043259ee430ecd71d178fccabd8c332a3bf1e81e50cae43cc2b28d19e4cb7"}, 611 | {file = "jiter-0.8.2-cp38-cp38-win32.whl", hash = "sha256:fc5adda618205bd4678b146612ce44c3cbfdee9697951f2c0ffdef1f26d72b63"}, 612 | {file = "jiter-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cd646c827b4f85ef4a78e4e58f4f5854fae0caf3db91b59f0d73731448a970c6"}, 613 | {file = "jiter-0.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e41e75344acef3fc59ba4765df29f107f309ca9e8eace5baacabd9217e52a5ee"}, 614 | {file = "jiter-0.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f22b16b35d5c1df9dfd58843ab2cd25e6bf15191f5a236bed177afade507bfc"}, 615 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7200b8f7619d36aa51c803fd52020a2dfbea36ffec1b5e22cab11fd34d95a6d"}, 616 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70bf4c43652cc294040dbb62256c83c8718370c8b93dd93d934b9a7bf6c4f53c"}, 617 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9d471356dc16f84ed48768b8ee79f29514295c7295cb41e1133ec0b2b8d637d"}, 618 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859e8eb3507894093d01929e12e267f83b1d5f6221099d3ec976f0c995cb6bd9"}, 619 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa58399c01db555346647a907b4ef6d4f584b123943be6ed5588c3f2359c9f4"}, 620 | {file = "jiter-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f2d5ed877f089862f4c7aacf3a542627c1496f972a34d0474ce85ee7d939c27"}, 621 | {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:03c9df035d4f8d647f8c210ddc2ae0728387275340668fb30d2421e17d9a0841"}, 622 | {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bd2a824d08d8977bb2794ea2682f898ad3d8837932e3a74937e93d62ecbb637"}, 623 | {file = "jiter-0.8.2-cp39-cp39-win32.whl", hash = "sha256:ca29b6371ebc40e496995c94b988a101b9fbbed48a51190a4461fcb0a68b4a36"}, 624 | {file = "jiter-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c0dfbd1be3cbefc7510102370d86e35d1d53e5a93d48519688b1bf0f761160a"}, 625 | {file = "jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d"}, 626 | ] 627 | 628 | [[package]] 629 | name = "markdown-it-py" 630 | version = "3.0.0" 631 | description = "Python port of markdown-it. Markdown parsing, done right!" 632 | optional = false 633 | python-versions = ">=3.8" 634 | groups = ["dev"] 635 | files = [ 636 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 637 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 638 | ] 639 | 640 | [package.dependencies] 641 | mdurl = ">=0.1,<1.0" 642 | 643 | [package.extras] 644 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 645 | code-style = ["pre-commit (>=3.0,<4.0)"] 646 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 647 | linkify = ["linkify-it-py (>=1,<3)"] 648 | plugins = ["mdit-py-plugins"] 649 | profiling = ["gprof2dot"] 650 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 651 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 652 | 653 | [[package]] 654 | name = "mccabe" 655 | version = "0.7.0" 656 | description = "McCabe checker, plugin for flake8" 657 | optional = false 658 | python-versions = ">=3.6" 659 | groups = ["dev"] 660 | files = [ 661 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 662 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 663 | ] 664 | 665 | [[package]] 666 | name = "mdurl" 667 | version = "0.1.2" 668 | description = "Markdown URL utilities" 669 | optional = false 670 | python-versions = ">=3.7" 671 | groups = ["dev"] 672 | files = [ 673 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 674 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 675 | ] 676 | 677 | [[package]] 678 | name = "mypy" 679 | version = "1.14.1" 680 | description = "Optional static typing for Python" 681 | optional = false 682 | python-versions = ">=3.8" 683 | groups = ["dev"] 684 | files = [ 685 | {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, 686 | {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, 687 | {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, 688 | {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, 689 | {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, 690 | {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, 691 | {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, 692 | {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, 693 | {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, 694 | {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, 695 | {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, 696 | {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, 697 | {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, 698 | {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, 699 | {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, 700 | {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, 701 | {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, 702 | {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, 703 | {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, 704 | {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, 705 | {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, 706 | {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, 707 | {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, 708 | {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, 709 | {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, 710 | {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, 711 | {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, 712 | {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, 713 | {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, 714 | {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, 715 | {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, 716 | {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, 717 | {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, 718 | {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, 719 | {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, 720 | {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, 721 | {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, 722 | {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, 723 | ] 724 | 725 | [package.dependencies] 726 | mypy_extensions = ">=1.0.0" 727 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 728 | typing_extensions = ">=4.6.0" 729 | 730 | [package.extras] 731 | dmypy = ["psutil (>=4.0)"] 732 | faster-cache = ["orjson"] 733 | install-types = ["pip"] 734 | mypyc = ["setuptools (>=50)"] 735 | reports = ["lxml"] 736 | 737 | [[package]] 738 | name = "mypy-extensions" 739 | version = "1.0.0" 740 | description = "Type system extensions for programs checked with the mypy type checker." 741 | optional = false 742 | python-versions = ">=3.5" 743 | groups = ["dev"] 744 | files = [ 745 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 746 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 747 | ] 748 | 749 | [[package]] 750 | name = "ollama" 751 | version = "0.4.7" 752 | description = "The official Python client for Ollama." 753 | optional = false 754 | python-versions = "<4.0,>=3.8" 755 | groups = ["main"] 756 | files = [ 757 | {file = "ollama-0.4.7-py3-none-any.whl", hash = "sha256:85505663cca67a83707be5fb3aeff0ea72e67846cea5985529d8eca4366564a1"}, 758 | {file = "ollama-0.4.7.tar.gz", hash = "sha256:891dcbe54f55397d82d289c459de0ea897e103b86a3f1fad0fdb1895922a75ff"}, 759 | ] 760 | 761 | [package.dependencies] 762 | httpx = ">=0.27,<0.29" 763 | pydantic = ">=2.9.0,<3.0.0" 764 | 765 | [[package]] 766 | name = "openai" 767 | version = "1.60.1" 768 | description = "The official Python library for the openai API" 769 | optional = false 770 | python-versions = ">=3.8" 771 | groups = ["main"] 772 | files = [ 773 | {file = "openai-1.60.1-py3-none-any.whl", hash = "sha256:714181ec1c452353d456f143c22db892de7b373e3165063d02a2b798ed575ba1"}, 774 | {file = "openai-1.60.1.tar.gz", hash = "sha256:beb1541dfc38b002bd629ab68b0d6fe35b870c5f4311d9bc4404d85af3214d5e"}, 775 | ] 776 | 777 | [package.dependencies] 778 | anyio = ">=3.5.0,<5" 779 | distro = ">=1.7.0,<2" 780 | httpx = ">=0.23.0,<1" 781 | jiter = ">=0.4.0,<1" 782 | pydantic = ">=1.9.0,<3" 783 | sniffio = "*" 784 | tqdm = ">4" 785 | typing-extensions = ">=4.11,<5" 786 | 787 | [package.extras] 788 | datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] 789 | realtime = ["websockets (>=13,<15)"] 790 | 791 | [[package]] 792 | name = "packaging" 793 | version = "24.2" 794 | description = "Core utilities for Python packages" 795 | optional = false 796 | python-versions = ">=3.8" 797 | groups = ["dev"] 798 | files = [ 799 | {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, 800 | {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, 801 | ] 802 | 803 | [[package]] 804 | name = "pathspec" 805 | version = "0.12.1" 806 | description = "Utility library for gitignore style pattern matching of file paths." 807 | optional = false 808 | python-versions = ">=3.8" 809 | groups = ["dev"] 810 | files = [ 811 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 812 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 813 | ] 814 | 815 | [[package]] 816 | name = "pbr" 817 | version = "6.1.0" 818 | description = "Python Build Reasonableness" 819 | optional = false 820 | python-versions = ">=2.6" 821 | groups = ["dev"] 822 | files = [ 823 | {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, 824 | {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, 825 | ] 826 | 827 | [[package]] 828 | name = "pep8-naming" 829 | version = "0.13.3" 830 | description = "Check PEP-8 naming conventions, plugin for flake8" 831 | optional = false 832 | python-versions = ">=3.7" 833 | groups = ["dev"] 834 | files = [ 835 | {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"}, 836 | {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"}, 837 | ] 838 | 839 | [package.dependencies] 840 | flake8 = ">=5.0.0" 841 | 842 | [[package]] 843 | name = "platformdirs" 844 | version = "4.3.6" 845 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 846 | optional = false 847 | python-versions = ">=3.8" 848 | groups = ["dev"] 849 | files = [ 850 | {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, 851 | {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, 852 | ] 853 | 854 | [package.extras] 855 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] 856 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] 857 | type = ["mypy (>=1.11.2)"] 858 | 859 | [[package]] 860 | name = "pluggy" 861 | version = "1.5.0" 862 | description = "plugin and hook calling mechanisms for python" 863 | optional = false 864 | python-versions = ">=3.8" 865 | groups = ["dev"] 866 | files = [ 867 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 868 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 869 | ] 870 | 871 | [package.extras] 872 | dev = ["pre-commit", "tox"] 873 | testing = ["pytest", "pytest-benchmark"] 874 | 875 | [[package]] 876 | name = "pycodestyle" 877 | version = "2.11.1" 878 | description = "Python style guide checker" 879 | optional = false 880 | python-versions = ">=3.8" 881 | groups = ["dev"] 882 | files = [ 883 | {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, 884 | {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, 885 | ] 886 | 887 | [[package]] 888 | name = "pydantic" 889 | version = "2.10.6" 890 | description = "Data validation using Python type hints" 891 | optional = false 892 | python-versions = ">=3.8" 893 | groups = ["main"] 894 | files = [ 895 | {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, 896 | {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, 897 | ] 898 | 899 | [package.dependencies] 900 | annotated-types = ">=0.6.0" 901 | pydantic-core = "2.27.2" 902 | typing-extensions = ">=4.12.2" 903 | 904 | [package.extras] 905 | email = ["email-validator (>=2.0.0)"] 906 | timezone = ["tzdata"] 907 | 908 | [[package]] 909 | name = "pydantic-core" 910 | version = "2.27.2" 911 | description = "Core functionality for Pydantic validation and serialization" 912 | optional = false 913 | python-versions = ">=3.8" 914 | groups = ["main"] 915 | files = [ 916 | {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, 917 | {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, 918 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, 919 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, 920 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, 921 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, 922 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, 923 | {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, 924 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, 925 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, 926 | {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, 927 | {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, 928 | {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, 929 | {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, 930 | {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, 931 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, 932 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, 933 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, 934 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, 935 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, 936 | {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, 937 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, 938 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, 939 | {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, 940 | {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, 941 | {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, 942 | {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, 943 | {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, 944 | {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, 945 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, 946 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, 947 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, 948 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, 949 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, 950 | {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, 951 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, 952 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, 953 | {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, 954 | {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, 955 | {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, 956 | {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, 957 | {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, 958 | {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, 959 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, 960 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, 961 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, 962 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, 963 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, 964 | {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, 965 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, 966 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, 967 | {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, 968 | {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, 969 | {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, 970 | {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, 971 | {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, 972 | {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, 973 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, 974 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, 975 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, 976 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, 977 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, 978 | {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, 979 | {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, 980 | {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, 981 | {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, 982 | {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, 983 | {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, 984 | {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, 985 | {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, 986 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, 987 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, 988 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, 989 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, 990 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, 991 | {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, 992 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, 993 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, 994 | {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, 995 | {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, 996 | {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, 997 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, 998 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, 999 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, 1000 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, 1001 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, 1002 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, 1003 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, 1004 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, 1005 | {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, 1006 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, 1007 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, 1008 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, 1009 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, 1010 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, 1011 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, 1012 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, 1013 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, 1014 | {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, 1015 | {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, 1016 | ] 1017 | 1018 | [package.dependencies] 1019 | typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" 1020 | 1021 | [[package]] 1022 | name = "pyflakes" 1023 | version = "3.1.0" 1024 | description = "passive checker of Python programs" 1025 | optional = false 1026 | python-versions = ">=3.8" 1027 | groups = ["dev"] 1028 | files = [ 1029 | {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, 1030 | {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, 1031 | ] 1032 | 1033 | [[package]] 1034 | name = "pygments" 1035 | version = "2.19.1" 1036 | description = "Pygments is a syntax highlighting package written in Python." 1037 | optional = false 1038 | python-versions = ">=3.8" 1039 | groups = ["dev"] 1040 | files = [ 1041 | {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, 1042 | {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, 1043 | ] 1044 | 1045 | [package.extras] 1046 | windows-terminal = ["colorama (>=0.4.6)"] 1047 | 1048 | [[package]] 1049 | name = "pynput" 1050 | version = "1.7.7" 1051 | description = "Monitor and control user input devices" 1052 | optional = false 1053 | python-versions = "*" 1054 | groups = ["main"] 1055 | files = [ 1056 | {file = "pynput-1.7.7-py2.py3-none-any.whl", hash = "sha256:afc43f651684c98818de048abc76adf9f2d3d797083cb07c1f82be764a2d44cb"}, 1057 | ] 1058 | 1059 | [package.dependencies] 1060 | evdev = {version = ">=1.3", markers = "sys_platform in \"linux\""} 1061 | pyobjc-framework-ApplicationServices = {version = ">=8.0", markers = "sys_platform == \"darwin\""} 1062 | pyobjc-framework-Quartz = {version = ">=8.0", markers = "sys_platform == \"darwin\""} 1063 | python-xlib = {version = ">=0.17", markers = "sys_platform in \"linux\""} 1064 | six = "*" 1065 | 1066 | [[package]] 1067 | name = "pyobjc-core" 1068 | version = "11.0" 1069 | description = "Python<->ObjC Interoperability Module" 1070 | optional = false 1071 | python-versions = ">=3.8" 1072 | groups = ["main"] 1073 | files = [ 1074 | {file = "pyobjc_core-11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:10866b3a734d47caf48e456eea0d4815c2c9b21856157db5917b61dee06893a1"}, 1075 | {file = "pyobjc_core-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:50675c0bb8696fe960a28466f9baf6943df2928a1fd85625d678fa2f428bd0bd"}, 1076 | {file = "pyobjc_core-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a03061d4955c62ddd7754224a80cdadfdf17b6b5f60df1d9169a3b1b02923f0b"}, 1077 | {file = "pyobjc_core-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c338c1deb7ab2e9436d4175d1127da2eeed4a1b564b3d83b9f3ae4844ba97e86"}, 1078 | {file = "pyobjc_core-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b4e9dc4296110f251a4033ff3f40320b35873ea7f876bd29a1c9705bb5e08c59"}, 1079 | {file = "pyobjc_core-11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:02406ece449d0f41b31e579e47ca77ced3eb57533df955281bfcecc99da74fba"}, 1080 | {file = "pyobjc_core-11.0.tar.gz", hash = "sha256:63bced211cb8a8fb5c8ff46473603da30e51112861bd02c438fbbbc8578d9a70"}, 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "pyobjc-framework-applicationservices" 1085 | version = "11.0" 1086 | description = "Wrappers for the framework ApplicationServices on macOS" 1087 | optional = false 1088 | python-versions = ">=3.9" 1089 | groups = ["main"] 1090 | markers = "sys_platform == \"darwin\"" 1091 | files = [ 1092 | {file = "pyobjc_framework_ApplicationServices-11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bc8f34b5b59ffd3c210ae883d794345c1197558ff3da0f5800669cf16435271e"}, 1093 | {file = "pyobjc_framework_ApplicationServices-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61a99eef23abb704257310db4f5271137707e184768f6407030c01de4731b67b"}, 1094 | {file = "pyobjc_framework_ApplicationServices-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5fbeb425897d6129471d451ec61a29ddd5b1386eb26b1dd49cb313e34616ee21"}, 1095 | {file = "pyobjc_framework_ApplicationServices-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:59becf3cd87a4f4cedf4be02ff6cf46ed736f5c1123ce629f788aaafad91eff0"}, 1096 | {file = "pyobjc_framework_ApplicationServices-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:44b466e8745fb49e8ac20f29f2ffd7895b45e97aa63a844b2a80a97c3a34346f"}, 1097 | {file = "pyobjc_framework_ApplicationServices-11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:74963e15a751d1454c1b8060914f116956e3a68f6a117c2163f491609125283b"}, 1098 | {file = "pyobjc_framework_applicationservices-11.0.tar.gz", hash = "sha256:d6ea18dfc7d5626a3ecf4ac72d510405c0d3a648ca38cae8db841acdebecf4d2"}, 1099 | ] 1100 | 1101 | [package.dependencies] 1102 | pyobjc-core = ">=11.0" 1103 | pyobjc-framework-Cocoa = ">=11.0" 1104 | pyobjc-framework-CoreText = ">=11.0" 1105 | pyobjc-framework-Quartz = ">=11.0" 1106 | 1107 | [[package]] 1108 | name = "pyobjc-framework-cocoa" 1109 | version = "11.0" 1110 | description = "Wrappers for the Cocoa frameworks on macOS" 1111 | optional = false 1112 | python-versions = ">=3.9" 1113 | groups = ["main"] 1114 | files = [ 1115 | {file = "pyobjc_framework_Cocoa-11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fbc65f260d617d5463c7fb9dbaaffc23c9a4fabfe3b1a50b039b61870b8daefd"}, 1116 | {file = "pyobjc_framework_Cocoa-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3ea7be6e6dd801b297440de02d312ba3fa7fd3c322db747ae1cb237e975f5d33"}, 1117 | {file = "pyobjc_framework_Cocoa-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:280a577b83c68175a28b2b7138d1d2d3111f2b2b66c30e86f81a19c2b02eae71"}, 1118 | {file = "pyobjc_framework_Cocoa-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:15b2bd977ed340074f930f1330f03d42912d5882b697d78bd06f8ebe263ef92e"}, 1119 | {file = "pyobjc_framework_Cocoa-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5750001db544e67f2b66f02067d8f0da96bb2ef71732bde104f01b8628f9d7ea"}, 1120 | {file = "pyobjc_framework_Cocoa-11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ddff25b0755d59873d186e1e07d6aaddb19d55e3ae890d69ff2d9babf8627657"}, 1121 | {file = "pyobjc_framework_cocoa-11.0.tar.gz", hash = "sha256:00346a8cb81ad7b017b32ff7bf596000f9faa905807b1bd234644ebd47f692c5"}, 1122 | ] 1123 | 1124 | [package.dependencies] 1125 | pyobjc-core = ">=11.0" 1126 | 1127 | [[package]] 1128 | name = "pyobjc-framework-coretext" 1129 | version = "11.0" 1130 | description = "Wrappers for the framework CoreText on macOS" 1131 | optional = false 1132 | python-versions = ">=3.9" 1133 | groups = ["main"] 1134 | markers = "sys_platform == \"darwin\"" 1135 | files = [ 1136 | {file = "pyobjc_framework_CoreText-11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6939b4ea745b349b5c964823a2071f155f5defdc9b9fc3a13f036d859d7d0439"}, 1137 | {file = "pyobjc_framework_CoreText-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:56a4889858308b0d9f147d568b4d91c441cc0ffd332497cb4f709bb1990450c1"}, 1138 | {file = "pyobjc_framework_CoreText-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fb90e7f370b3fd7cb2fb442e3dc63fedf0b4af6908db1c18df694d10dc94669d"}, 1139 | {file = "pyobjc_framework_CoreText-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7947f755782456bd663e0b00c7905eeffd10f839f0bf2af031f68ded6a1ea360"}, 1140 | {file = "pyobjc_framework_CoreText-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5356116bae33ec49f1f212c301378a7d08000440a2d6a7281aab351945528ab9"}, 1141 | {file = "pyobjc_framework_CoreText-11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4a76e1307747f2ee8180d38844cd62b8bb1701b4203d9234cc41f6603d4ae654"}, 1142 | {file = "pyobjc_framework_coretext-11.0.tar.gz", hash = "sha256:a68437153e627847e3898754dd3f13ae0cb852246b016a91f9c9cbccb9f91a43"}, 1143 | ] 1144 | 1145 | [package.dependencies] 1146 | pyobjc-core = ">=11.0" 1147 | pyobjc-framework-Cocoa = ">=11.0" 1148 | pyobjc-framework-Quartz = ">=11.0" 1149 | 1150 | [[package]] 1151 | name = "pyobjc-framework-quartz" 1152 | version = "11.0" 1153 | description = "Wrappers for the Quartz frameworks on macOS" 1154 | optional = false 1155 | python-versions = ">=3.9" 1156 | groups = ["main"] 1157 | markers = "sys_platform == \"darwin\"" 1158 | files = [ 1159 | {file = "pyobjc_framework_Quartz-11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da3ab13c9f92361959b41b0ad4cdd41ae872f90a6d8c58a9ed699bc08ab1c45c"}, 1160 | {file = "pyobjc_framework_Quartz-11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d251696bfd8e8ef72fbc90eb29fec95cb9d1cc409008a183d5cc3246130ae8c2"}, 1161 | {file = "pyobjc_framework_Quartz-11.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cb4a9f2d9d580ea15e25e6b270f47681afb5689cafc9e25712445ce715bcd18e"}, 1162 | {file = "pyobjc_framework_Quartz-11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:973b4f9b8ab844574461a038bd5269f425a7368d6e677e3cc81fcc9b27b65498"}, 1163 | {file = "pyobjc_framework_Quartz-11.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:66ab58d65348863b8707e63b2ec5cdc54569ee8189d1af90d52f29f5fdf6272c"}, 1164 | {file = "pyobjc_framework_Quartz-11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1032f63f2a4ee98366764e69c249f1d93813821e17d224cf626cf11fb1801fc4"}, 1165 | {file = "pyobjc_framework_quartz-11.0.tar.gz", hash = "sha256:3205bf7795fb9ae34747f701486b3db6dfac71924894d1f372977c4d70c3c619"}, 1166 | ] 1167 | 1168 | [package.dependencies] 1169 | pyobjc-core = ">=11.0" 1170 | pyobjc-framework-Cocoa = ">=11.0" 1171 | 1172 | [[package]] 1173 | name = "pyperclip" 1174 | version = "1.9.0" 1175 | description = "A cross-platform clipboard module for Python. (Only handles plain text for now.)" 1176 | optional = false 1177 | python-versions = "*" 1178 | groups = ["main"] 1179 | files = [ 1180 | {file = "pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"}, 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "pyqt6" 1185 | version = "6.8.0" 1186 | description = "Python bindings for the Qt cross platform application toolkit" 1187 | optional = false 1188 | python-versions = ">=3.9" 1189 | groups = ["main"] 1190 | files = [ 1191 | {file = "PyQt6-6.8.0-cp39-abi3-macosx_10_14_universal2.whl", hash = "sha256:8c5c05f5fdff31a5887dbc29b27615b09df467631238d7b449283809ffca6228"}, 1192 | {file = "PyQt6-6.8.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3a4354816f11e812b727206a9ea6e79ff3774f1bb7228ad4b9318442d2c64ff9"}, 1193 | {file = "PyQt6-6.8.0-cp39-abi3-manylinux_2_35_x86_64.whl", hash = "sha256:452bae5840077bf0f146c798d7777f70d7bdd0c7dcfa9ee7a415c1daf2d10038"}, 1194 | {file = "PyQt6-6.8.0-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:cf7123caea14e7ecf10bd12cae48e8d9970ef7caf627bc7d7988b0baa209adb3"}, 1195 | {file = "PyQt6-6.8.0-cp39-abi3-win_amd64.whl", hash = "sha256:a9913d479f1ffee804bf7f232079baea4fb4b221a8f4890117588917a54ea30d"}, 1196 | {file = "PyQt6-6.8.0-cp39-abi3-win_arm64.whl", hash = "sha256:48bace7b87676bba5e6114482f3a20ca20be90c7f261b5d340464313f5f2bf5e"}, 1197 | {file = "PyQt6-6.8.0.tar.gz", hash = "sha256:6d8628de4c2a050f0b74462e4c9cb97f839bf6ffabbca91711722ffb281570d9"}, 1198 | ] 1199 | 1200 | [package.dependencies] 1201 | PyQt6-Qt6 = ">=6.8.0,<6.9.0" 1202 | PyQt6-sip = ">=13.8,<14" 1203 | 1204 | [[package]] 1205 | name = "pyqt6-qt6" 1206 | version = "6.8.1" 1207 | description = "The subset of a Qt installation needed by PyQt6." 1208 | optional = false 1209 | python-versions = "*" 1210 | groups = ["main"] 1211 | files = [ 1212 | {file = "PyQt6_Qt6-6.8.1-1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:2f4b8b55b1414b93f340f22e8c88d25550efcdebc4b65a3927dd947b73bd4358"}, 1213 | {file = "PyQt6_Qt6-6.8.1-1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:98aa99fe38ae68c5318284cd28f3479ba538c40bf6ece293980abae0925c1b24"}, 1214 | {file = "PyQt6_Qt6-6.8.1-py3-none-macosx_10_14_x86_64.whl", hash = "sha256:1eb8460a1fdb38d0b2458c2974c01d471c1e59e4eb19ea63fc447aaba3ad530e"}, 1215 | {file = "PyQt6_Qt6-6.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f3790c4ce4dc576e48b8718d55fb8743057e6cbd53a6ca1dd253ffbac9b7287"}, 1216 | {file = "PyQt6_Qt6-6.8.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d6ca5d2b9d2ec0ee4a814b2175f641a5c4299cb80b45e0f5f8356632663f89b3"}, 1217 | {file = "PyQt6_Qt6-6.8.1-py3-none-manylinux_2_35_x86_64.whl", hash = "sha256:08065d595f1e6fc2dde9f4450eeff89082f4bad26f600a8e9b9cc5966716bfcf"}, 1218 | {file = "PyQt6_Qt6-6.8.1-py3-none-manylinux_2_39_aarch64.whl", hash = "sha256:20843cb86bd94942d1cd99e39bf1aeabb875b241a35a8ab273e4bbbfa63776db"}, 1219 | {file = "PyQt6_Qt6-6.8.1-py3-none-win_amd64.whl", hash = "sha256:006d786693d0511fbcf184a862edbd339c6ed1bb3bd9de363d73a19ed4b23dff"}, 1220 | {file = "PyQt6_Qt6-6.8.1-py3-none-win_arm64.whl", hash = "sha256:a8bc2ed4ee5e7c6ff4dd1c7db0b27705d151fee5dc232bbd1bf17618f937f515"}, 1221 | ] 1222 | 1223 | [[package]] 1224 | name = "pyqt6-sip" 1225 | version = "13.9.1" 1226 | description = "The sip module support for PyQt6" 1227 | optional = false 1228 | python-versions = ">=3.9" 1229 | groups = ["main"] 1230 | files = [ 1231 | {file = "PyQt6_sip-13.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e996d320744ca8342cad6f9454345330d4f06bce129812d032bda3bad6967c5c"}, 1232 | {file = "PyQt6_sip-13.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ab85aaf155828331399c59ebdd4d3b0358e42c08250e86b43d56d9873df148a"}, 1233 | {file = "PyQt6_sip-13.9.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22d66256b800f552ade51a463510bf905f3cb318aae00ff4288fae4de5d0e600"}, 1234 | {file = "PyQt6_sip-13.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:5643c92424fe62cb0b33378fef3d28c1525f91ada79e8a15bd9a05414a09503d"}, 1235 | {file = "PyQt6_sip-13.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:57b5312ef13c1766bdf69b317041140b184eb24a51e1e23ce8fc5386ba8dffb2"}, 1236 | {file = "PyQt6_sip-13.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5004514b08b045ad76425cf3618187091a668d972b017677b1b4b193379ef553"}, 1237 | {file = "PyQt6_sip-13.9.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:accab6974b2758296400120fdcc9d1f37785b2ea2591f00656e1776f058ded6c"}, 1238 | {file = "PyQt6_sip-13.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:1ec52e962f54137a19208b6e95b6bd9f7a403eb25d7237768a99306cd9db26d1"}, 1239 | {file = "PyQt6_sip-13.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:6e6c1e2592187934f4e790c0c099d0033e986dcef7bdd3c06e3895ffa995e9fc"}, 1240 | {file = "PyQt6_sip-13.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1fb405615970e85b622b13b4cad140ff1e4182eb8334a0b27a4698e6217b89b0"}, 1241 | {file = "PyQt6_sip-13.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c800db3464481e87b1d2b84523b075df1e8fc7856c6f9623dc243f89be1cb604"}, 1242 | {file = "PyQt6_sip-13.9.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c1942e107b0243ced9e510d507e0f27aeea9d6b13e0a1b7c06fd52a62e0d41f7"}, 1243 | {file = "PyQt6_sip-13.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:552ff8fdc41f5769d3eccc661f022ed496f55f6e0a214c20aaf56e56385d61b6"}, 1244 | {file = "PyQt6_sip-13.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:976c7758f668806d4df7a8853f390ac123d5d1f73591ed368bdb8963574ff589"}, 1245 | {file = "PyQt6_sip-13.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:56ce0afb19cd8a8c63ff93ae506dffb74f844b88adaa6673ebc0dec43af48a76"}, 1246 | {file = "PyQt6_sip-13.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d7726556d1ca7a7ed78e19ba53285b64a2a8f6ad7ff4cb18a1832efca1a3102"}, 1247 | {file = "PyQt6_sip-13.9.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14f95c6352e3b85dc26bf59cfbf77a470ecbd5fcdcf00af4b648f0e1b9eefb9e"}, 1248 | {file = "PyQt6_sip-13.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:3c269052c770c09b61fce2f2f9ea934a67dfc65f443d59629b4ccc8f89751890"}, 1249 | {file = "PyQt6_sip-13.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:8b2ac36d6e04db6099614b9c1178a2f87788c7ffc3826571fb63d36ddb4c401d"}, 1250 | {file = "PyQt6_sip-13.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:69a879cfc94f4984d180321b76f52923861cd5bf4969aa885eef7591ee932517"}, 1251 | {file = "PyQt6_sip-13.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa27b51ae4c7013b3700cf0ecf46907d1333ae396fc6511311920485cbce094b"}, 1252 | {file = "PyQt6_sip-13.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1d322ded1d1fea339cc6ac65b768e72c69c486eebb7db6ccde061b5786d74cc5"}, 1253 | {file = "PyQt6_sip-13.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:8c207528992d59b0801458aa6fcff118e5c099608ef0fc6ff8bccbdc23f29c04"}, 1254 | {file = "pyqt6_sip-13.9.1.tar.gz", hash = "sha256:15be741d1ae8c82bb7afe9a61f3cf8c50457f7d61229a1c39c24cd6e8f4d86dc"}, 1255 | ] 1256 | 1257 | [[package]] 1258 | name = "pytest" 1259 | version = "7.4.4" 1260 | description = "pytest: simple powerful testing with Python" 1261 | optional = false 1262 | python-versions = ">=3.7" 1263 | groups = ["dev"] 1264 | files = [ 1265 | {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, 1266 | {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, 1267 | ] 1268 | 1269 | [package.dependencies] 1270 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 1271 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 1272 | iniconfig = "*" 1273 | packaging = "*" 1274 | pluggy = ">=0.12,<2.0" 1275 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 1276 | 1277 | [package.extras] 1278 | testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 1279 | 1280 | [[package]] 1281 | name = "pytest-cov" 1282 | version = "4.1.0" 1283 | description = "Pytest plugin for measuring coverage." 1284 | optional = false 1285 | python-versions = ">=3.7" 1286 | groups = ["dev"] 1287 | files = [ 1288 | {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, 1289 | {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, 1290 | ] 1291 | 1292 | [package.dependencies] 1293 | coverage = {version = ">=5.2.1", extras = ["toml"]} 1294 | pytest = ">=4.6" 1295 | 1296 | [package.extras] 1297 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 1298 | 1299 | [[package]] 1300 | name = "python-xlib" 1301 | version = "0.33" 1302 | description = "Python X Library" 1303 | optional = false 1304 | python-versions = "*" 1305 | groups = ["main"] 1306 | markers = "sys_platform in \"linux\"" 1307 | files = [ 1308 | {file = "python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32"}, 1309 | {file = "python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398"}, 1310 | ] 1311 | 1312 | [package.dependencies] 1313 | six = ">=1.10.0" 1314 | 1315 | [[package]] 1316 | name = "pyyaml" 1317 | version = "6.0.2" 1318 | description = "YAML parser and emitter for Python" 1319 | optional = false 1320 | python-versions = ">=3.8" 1321 | groups = ["dev"] 1322 | files = [ 1323 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 1324 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 1325 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 1326 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 1327 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 1328 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 1329 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 1330 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 1331 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 1332 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 1333 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 1334 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 1335 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 1336 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 1337 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 1338 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 1339 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 1340 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 1341 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 1342 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 1343 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 1344 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 1345 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 1346 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 1347 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 1348 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 1349 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 1350 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 1351 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 1352 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 1353 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 1354 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 1355 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 1356 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 1357 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 1358 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 1359 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 1360 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 1361 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 1362 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 1363 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 1364 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 1365 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 1366 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 1367 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 1368 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 1369 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 1370 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 1371 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 1372 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 1373 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 1374 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 1375 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 1376 | ] 1377 | 1378 | [[package]] 1379 | name = "rich" 1380 | version = "13.9.4" 1381 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 1382 | optional = false 1383 | python-versions = ">=3.8.0" 1384 | groups = ["dev"] 1385 | files = [ 1386 | {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, 1387 | {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, 1388 | ] 1389 | 1390 | [package.dependencies] 1391 | markdown-it-py = ">=2.2.0" 1392 | pygments = ">=2.13.0,<3.0.0" 1393 | typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} 1394 | 1395 | [package.extras] 1396 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 1397 | 1398 | [[package]] 1399 | name = "six" 1400 | version = "1.17.0" 1401 | description = "Python 2 and 3 compatibility utilities" 1402 | optional = false 1403 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 1404 | groups = ["main"] 1405 | files = [ 1406 | {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, 1407 | {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "sniffio" 1412 | version = "1.3.1" 1413 | description = "Sniff out which async library your code is running under" 1414 | optional = false 1415 | python-versions = ">=3.7" 1416 | groups = ["main"] 1417 | files = [ 1418 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 1419 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 1420 | ] 1421 | 1422 | [[package]] 1423 | name = "stevedore" 1424 | version = "5.4.0" 1425 | description = "Manage dynamic plugins for Python applications" 1426 | optional = false 1427 | python-versions = ">=3.9" 1428 | groups = ["dev"] 1429 | files = [ 1430 | {file = "stevedore-5.4.0-py3-none-any.whl", hash = "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857"}, 1431 | {file = "stevedore-5.4.0.tar.gz", hash = "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d"}, 1432 | ] 1433 | 1434 | [package.dependencies] 1435 | pbr = ">=2.0.0" 1436 | 1437 | [[package]] 1438 | name = "tomli" 1439 | version = "2.2.1" 1440 | description = "A lil' TOML parser" 1441 | optional = false 1442 | python-versions = ">=3.8" 1443 | groups = ["dev"] 1444 | markers = "python_full_version <= \"3.11.0a6\"" 1445 | files = [ 1446 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 1447 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 1448 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 1449 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 1450 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 1451 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 1452 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 1453 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 1454 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 1455 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 1456 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 1457 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 1458 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 1459 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 1460 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 1461 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 1462 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 1463 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 1464 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 1465 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 1466 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 1467 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 1468 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 1469 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 1470 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 1471 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 1472 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 1473 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 1474 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 1475 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 1476 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 1477 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 1478 | ] 1479 | 1480 | [[package]] 1481 | name = "tqdm" 1482 | version = "4.67.1" 1483 | description = "Fast, Extensible Progress Meter" 1484 | optional = false 1485 | python-versions = ">=3.7" 1486 | groups = ["main"] 1487 | files = [ 1488 | {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, 1489 | {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, 1490 | ] 1491 | 1492 | [package.dependencies] 1493 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 1494 | 1495 | [package.extras] 1496 | dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] 1497 | discord = ["requests"] 1498 | notebook = ["ipywidgets (>=6)"] 1499 | slack = ["slack-sdk"] 1500 | telegram = ["requests"] 1501 | 1502 | [[package]] 1503 | name = "typing-extensions" 1504 | version = "4.12.2" 1505 | description = "Backported and Experimental Type Hints for Python 3.8+" 1506 | optional = false 1507 | python-versions = ">=3.8" 1508 | groups = ["main", "dev"] 1509 | files = [ 1510 | {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, 1511 | {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, 1512 | ] 1513 | 1514 | [metadata] 1515 | lock-version = "2.1" 1516 | python-versions = "^3.9" 1517 | content-hash = "3c58bca5a5735515ad982c0bc81f2352c690724876a6dc83d9fcd669fdeaae55" 1518 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "orange-intelligence" 3 | version = "0.1.0" 4 | description = "The Orange Intelligence for mac os x" 5 | authors = ["Pietro"] 6 | readme = "README.md" 7 | packages = [ 8 | { include = "core" }, 9 | { include = "extensions" }, 10 | ] 11 | 12 | [tool.poetry.dependencies] 13 | python = "^3.9" 14 | pynput = "1.7.7" 15 | pyperclip="1.9.0" 16 | PyQt6="6.8.0" 17 | ollama="0.4.7" 18 | pyobjc-framework-Cocoa="11.0" 19 | openai="1.60.1" 20 | 21 | [tool.poetry.dev-dependencies] 22 | black = "^23.7.0" 23 | flake8 = "^6.1.0" 24 | isort = "^5.12.0" 25 | mypy = "^1.8.0" 26 | pytest = "^7.4.0" 27 | pytest-cov = "^4.1.0" 28 | flake8-eradicate = "^1.5.0" 29 | flake8-pytest-style = "^1.7.2" 30 | pep8-naming = "^0.13.3" 31 | flake8-bugbear = "^24.2.6" 32 | flake8-annotations = "^3.0.1" 33 | flake8-simplify = "^0.21.0" 34 | flake8-print = "^5.0.0" 35 | bandit = {extras = ["toml"], version = "^1.7.7"} 36 | 37 | 38 | [build-system] 39 | requires = ["poetry-core"] 40 | build-backend = "poetry.core.masonry.api" 41 | 42 | 43 | [tool.black] 44 | line-length = 120 45 | target-version = ['py312'] 46 | include = '\.pyi?$' 47 | exclude = ''' 48 | ^/( 49 | ( 50 | \.eggs 51 | | .venv 52 | | \.git 53 | | build 54 | | dist 55 | | notebooks 56 | ) 57 | ) 58 | ''' 59 | 60 | [tool.isort] 61 | line_length = 120 62 | profile = "black" 63 | src_paths = ["core", "tests", "extensions"] 64 | 65 | [tool.pytest.ini_options] 66 | minversion = "6.0" 67 | testpaths = ["tests"] 68 | python_files = ["tests.py", "test_*.py", "*_tests.py"] 69 | 70 | [tool.coverage.report] 71 | exclude_also = [ 72 | "if __name__ == .__main__.:", 73 | "if TYPE_CHECKING:", 74 | ] 75 | 76 | [tool.mypy] 77 | files = ["core/**/*.py", "extensions/**/*.py", "tests/**/*.py", "utils/**/*.py"] 78 | follow_imports = "normal" 79 | strict_optional = "False" 80 | warn_redundant_casts = "True" 81 | warn_unused_ignores = "True" 82 | disallow_any_generics = "False" 83 | check_untyped_defs = "True" 84 | no_implicit_reexport = "True" 85 | no_implicit_optional = "True" 86 | disallow_untyped_defs = "False" 87 | ignore_missing_imports = "True" 88 | namespace_packages = "True" 89 | disallow_any_unimported = "True" 90 | exclude = [] 91 | 92 | [tool.bandit] 93 | exclude_dirs = ["tests", ".venv", "dist"] -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharingan-no-kakashi/orange-intelligence/02e7d3ff8e80f2b84a15d674a94b478bd61762d0/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_dummy.py: -------------------------------------------------------------------------------- 1 | def test_dummy_assert_true(): 2 | assert True 3 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import logging 3 | import os 4 | import pkgutil 5 | import subprocess 6 | import sys 7 | import time 8 | import types 9 | import typing 10 | 11 | import pyperclip 12 | from AppKit import NSApp 13 | from PyQt6.QtCore import QProcess 14 | 15 | LOG = logging.getLogger(__name__) 16 | 17 | 18 | def get_current_process_id() -> str: 19 | return f"{os.getpid()}" 20 | 21 | 22 | def get_focused_text() -> str: 23 | cmd_c() 24 | time.sleep(0.3) 25 | # Get clipboard content 26 | clipboard_content = pyperclip.paste() 27 | return clipboard_content.strip() 28 | 29 | 30 | def cmd_v() -> None: 31 | applescript = """ 32 | tell application "System Events" 33 | keystroke "v" using {command down} 34 | end tell 35 | """ 36 | subprocess.run(["osascript", "-e", applescript]) 37 | 38 | 39 | def return_app_in_focus() -> str: 40 | command = """/usr/bin/osascript -e 'tell application "System Events" 41 | set frontApp to first application process whose frontmost is true 42 | return unix id of frontApp 43 | end tell' """ 44 | res = subprocess.run(command, shell=True, stdin=sys.stdin, stdout=subprocess.PIPE, stderr=sys.stderr, text=True) 45 | return res.stdout.strip() 46 | 47 | 48 | def cmd_c() -> None: 49 | applescript = """ 50 | tell application "System Events" 51 | keystroke "c" using {command down} 52 | end tell 53 | """ 54 | subprocess.run(["osascript", "-e", applescript]) 55 | 56 | 57 | def put_app_in_focus(process_id: str) -> None: 58 | script = f"""tell application "System Events" 59 | set frontmost of (first process whose unix id is {process_id}) to true 60 | end tell""" 61 | QProcess.startDetached("/usr/bin/osascript", ["-e", script]) 62 | 63 | 64 | def put_this_app_in_focus() -> None: 65 | this_process_id = get_current_process_id() 66 | return put_app_in_focus(this_process_id) 67 | 68 | 69 | def import_package_init_functions(package: types.ModuleType) -> dict[str, dict[str, typing.Callable]]: 70 | # List all submodules (modules and subpackages) in the package 71 | submodules = [module.name for module in pkgutil.iter_modules(package.__path__)] 72 | callables = {} 73 | 74 | for submodule in submodules: 75 | # Dynamically import the __init__.py of the subpackage 76 | try: 77 | subpackage = importlib.import_module(f"{package.__name__}.{submodule}") 78 | if hasattr(subpackage, "__init__"): 79 | # Get all callables (functions and callable objects) in the __init__.py of the subpackage 80 | subpackage_callables = { 81 | item: getattr(subpackage, item) 82 | for item in dir(subpackage) 83 | if callable(getattr(subpackage, item)) 84 | if not item.startswith("_") 85 | } 86 | 87 | # Store callables with their names 88 | callables[submodule] = subpackage_callables 89 | 90 | except Exception as e: 91 | LOG.error(f"Error importing {submodule}: {e}") 92 | 93 | return callables 94 | 95 | 96 | def load_all_available_functions(package: types.ModuleType) -> dict[str, dict[str, typing.Callable]]: 97 | return {name: functions for name, functions in import_package_init_functions(package).items() if len(functions) > 0} 98 | 99 | 100 | def avoid_dock_macos_icon(): 101 | NSApp.setActivationPolicy_(1) 102 | --------------------------------------------------------------------------------