• 0 Posts
  • 17 Comments
Joined 2 years ago
cake
Cake day: June 10th, 2023

help-circle






  • If you look at the repo, the very first line in the readme links to an issue that briefly explains why you should care.

    Unmaintained software comes in two categories:

    1. The software is done. It does exactly what it was meant to do and it was written in a language and in such a way as to be pretty future proof. Examples are some basic code libraries or command line utilities.
    2. The software had to be updated all the time to keep up with changing environments and security problems, so the dev got sick of it and dropped it. Or a better solution came along so the developer felt free to finally drop the burden.

    Nativefier falls in the second category and the second clause. Don’t use it.


  • You asked for my python script but now I can’t seem to load that comment to reply directly to it. Anyway, here’s the script, I haven’t bothered to upload the repo anywhere. I’m sure it isn’t perfect but it works fine for me. The action for opening evolution when you click the tray icon is specific to hyprland so will probably need to be modified to suit your needs.

    import asyncio
    import concurrent.futures
    import logging
    import signal
    import sqlite3
    import sys
    from pathlib import Path
    from subprocess import run
    
    import pkg_resources
    from inotify_simple import INotify, flags
    from PySimpleGUIQt import SystemTray
    
    menu_def = ["BLANK", ["Exit"]]
    
    empty_icon = pkg_resources.resource_filename(
        "evolution_tray", "resources/inbox-empty.svg"
    )
    full_icon = pkg_resources.resource_filename(
        "evolution_tray", "resources/inbox-full.svg"
    )
    
    inotify = INotify()
    
    tray = SystemTray(filename=empty_icon, menu=menu_def, tooltip="Inbox empty")
    
    logging.getLogger("asyncio").setLevel(logging.WARNING)
    handler = logging.StreamHandler(sys.stdout)
    logger = logging.getLogger()
    logger.setLevel("DEBUG")
    logger.addHandler(handler)
    
    
    def handle_menu_events():
        while True:
            menu_item = tray.read()
            if menu_item == "Exit":
                signal.raise_signal(signal.SIGTERM)
            elif menu_item == "__ACTIVATED__":
                run(["hyprctl", "dispatch", "exec", "evolution"])
                # tray.update(filename=paused_icon)
    
            logger.info("Opened evolution")
    
    
    def get_all_databases():
        cache_path = Path.home() / ".cache" / "evolution" / "mail"
        return list(cache_path.glob("**/folders.db"))
    
    
    def check_unread() -> int:
        unread = 0
        for db in get_all_databases():
            conn = sqlite3.connect(db)
            cursor = conn.cursor()
            try:
                cursor.execute("select count(*) read from INBOX where read == 0")
                unread += cursor.fetchone()[0]
            except:
                pass
            finally:
                conn.close()
    
        if unread > 0:
            tray.update(filename=full_icon, tooltip=f"{unread} unread emails")
        else:
            tray.update(filename=empty_icon, tooltip="Inbox empty")
    
        return unread
    
    
    def watch_inbox():
        while True:
            for database in get_all_databases():
                inotify.add_watch(database, mask=flags.MODIFY)
            while inotify.read():
                logger.info("New mail")
                logger.info(f"{check_unread()} new emails")
    
    
    async def main():
        executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
        loop = asyncio.get_running_loop()
        check_unread()
    
        watch_task = asyncio.wait(
            fs={
                loop.run_in_executor(executor, watch_inbox),
            },
            return_when=asyncio.FIRST_COMPLETED,
        )
        await asyncio.gather(watch_task, loop.create_task(handle_menu_events()))
    
    
    def entrypoint():
        signal.signal(signal.SIGINT, signal.SIG_DFL)
        signal.signal(signal.SIGTERM, signal.SIG_DFL)
    
        try:
            asyncio.run(main())
        except Exception as e:
            logger.exception(e)
    
    
    if __name__ == "__main__":
        entrypoint()
    






  • Well at least php has it, which is a JITed scripting language just like Python. Although saying php has it is wrong, it’s just a special doc tag that the linters pick up. Which is exactly what I want for Python. The only other scripting language I’m very comfortable with is typescript, which can also support @throws via jsdoc and eslint.

    So to answer your question, I don’t know if it’s common, but from my minimal sample pool it’s at least not unheard of.

    You may not know this (just guessing because you commented on the nature of scripting/interpreted languages) but static analysis of dynamic languages has come really far and is an indispensable part of any reasonably sized project written in them these days. That’s another reason why I’m so surprised and frustrated by the lack of this in Python.




  • Day 598 of asking for a way to tell which functions throw exceptions in Python so I can know when to wrap in try catch. Seems to me that every other language has this, but when I’ve asked for at least a linter that can tell me I’m calling a function that throws, the general answer has been “why would you want that?”

    How am I supposed to ask for forgiveness if it’s impossible to know that I’m doing something risky in the first place?